xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/wgpu/BufferWgpu.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2024 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // BufferWgpu.cpp:
7 //    Implements the class methods for BufferWgpu.
8 //
9 
10 #include "libANGLE/renderer/wgpu/BufferWgpu.h"
11 
12 #include "common/debug.h"
13 #include "common/mathutil.h"
14 #include "common/utilities.h"
15 #include "libANGLE/Context.h"
16 #include "libANGLE/angletypes.h"
17 #include "libANGLE/renderer/wgpu/ContextWgpu.h"
18 #include "libANGLE/renderer/wgpu/wgpu_utils.h"
19 
20 namespace rx
21 {
22 namespace
23 {
24 // Based on a buffer binding target, compute the default wgpu usage flags. More can be added if the
25 // buffer is used in new ways.
GetDefaultWGPUBufferUsageForBinding(gl::BufferBinding binding)26 wgpu::BufferUsage GetDefaultWGPUBufferUsageForBinding(gl::BufferBinding binding)
27 {
28     switch (binding)
29     {
30         case gl::BufferBinding::Array:
31         case gl::BufferBinding::ElementArray:
32             return wgpu::BufferUsage::Vertex | wgpu::BufferUsage::Index |
33                    wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
34 
35         case gl::BufferBinding::Uniform:
36             return wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopySrc |
37                    wgpu::BufferUsage::CopyDst;
38 
39         case gl::BufferBinding::PixelPack:
40             return wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
41 
42         case gl::BufferBinding::PixelUnpack:
43             return wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc;
44 
45         case gl::BufferBinding::CopyRead:
46         case gl::BufferBinding::CopyWrite:
47         case gl::BufferBinding::ShaderStorage:
48         case gl::BufferBinding::Texture:
49         case gl::BufferBinding::TransformFeedback:
50         case gl::BufferBinding::DispatchIndirect:
51         case gl::BufferBinding::DrawIndirect:
52         case gl::BufferBinding::AtomicCounter:
53             UNIMPLEMENTED();
54             return wgpu::BufferUsage::None;
55 
56         default:
57             UNREACHABLE();
58             return wgpu::BufferUsage::None;
59     }
60 }
61 
62 }  // namespace
63 
BufferWgpu(const gl::BufferState & state)64 BufferWgpu::BufferWgpu(const gl::BufferState &state) : BufferImpl(state) {}
65 
~BufferWgpu()66 BufferWgpu::~BufferWgpu() {}
67 
setData(const gl::Context * context,gl::BufferBinding target,const void * data,size_t size,gl::BufferUsage usage)68 angle::Result BufferWgpu::setData(const gl::Context *context,
69                                   gl::BufferBinding target,
70                                   const void *data,
71                                   size_t size,
72                                   gl::BufferUsage usage)
73 {
74     ContextWgpu *contextWgpu = webgpu::GetImpl(context);
75     wgpu::Device device      = webgpu::GetDevice(context);
76 
77     bool hasData = data && size > 0;
78 
79     // Allocate a new buffer if the current one is invalid, the size is different, or the current
80     // buffer cannot be mapped for writing when data needs to be uploaded.
81     if (!mBuffer.valid() || mBuffer.requestedSize() != size ||
82         (hasData && !mBuffer.canMapForWrite()))
83     {
84         // Allocate a new buffer
85         ANGLE_TRY(mBuffer.initBuffer(device, size, GetDefaultWGPUBufferUsageForBinding(target),
86                                      webgpu::MapAtCreation::Yes));
87     }
88 
89     if (hasData)
90     {
91         ASSERT(mBuffer.canMapForWrite());
92 
93         if (!mBuffer.getMappedState().has_value())
94         {
95             ANGLE_TRY(mBuffer.mapImmediate(contextWgpu, wgpu::MapMode::Write, 0, size));
96         }
97 
98         uint8_t *mappedData = mBuffer.getMapWritePointer(0, size);
99         memcpy(mappedData, data, size);
100     }
101 
102     return angle::Result::Continue;
103 }
104 
setSubData(const gl::Context * context,gl::BufferBinding target,const void * data,size_t size,size_t offset)105 angle::Result BufferWgpu::setSubData(const gl::Context *context,
106                                      gl::BufferBinding target,
107                                      const void *data,
108                                      size_t size,
109                                      size_t offset)
110 {
111     ContextWgpu *contextWgpu = webgpu::GetImpl(context);
112     wgpu::Device device      = webgpu::GetDevice(context);
113 
114     ASSERT(mBuffer.valid());
115     if (mBuffer.canMapForWrite())
116     {
117         if (!mBuffer.getMappedState().has_value())
118         {
119             ANGLE_TRY(mBuffer.mapImmediate(contextWgpu, wgpu::MapMode::Write, offset, size));
120         }
121 
122         uint8_t *mappedData = mBuffer.getMapWritePointer(offset, size);
123         memcpy(mappedData, data, size);
124     }
125     else
126     {
127         // TODO: Upload into a staging buffer and copy to the destination buffer so that the copy
128         // happens at the right point in time for command buffer recording.
129         wgpu::Queue &queue = contextWgpu->getQueue();
130         queue.WriteBuffer(mBuffer.getBuffer(), offset, data, size);
131     }
132 
133     return angle::Result::Continue;
134 }
135 
copySubData(const gl::Context * context,BufferImpl * source,GLintptr sourceOffset,GLintptr destOffset,GLsizeiptr size)136 angle::Result BufferWgpu::copySubData(const gl::Context *context,
137                                       BufferImpl *source,
138                                       GLintptr sourceOffset,
139                                       GLintptr destOffset,
140                                       GLsizeiptr size)
141 {
142     return angle::Result::Continue;
143 }
144 
map(const gl::Context * context,GLenum access,void ** mapPtr)145 angle::Result BufferWgpu::map(const gl::Context *context, GLenum access, void **mapPtr)
146 {
147     return angle::Result::Continue;
148 }
149 
mapRange(const gl::Context * context,size_t offset,size_t length,GLbitfield access,void ** mapPtr)150 angle::Result BufferWgpu::mapRange(const gl::Context *context,
151                                    size_t offset,
152                                    size_t length,
153                                    GLbitfield access,
154                                    void **mapPtr)
155 {
156     return angle::Result::Continue;
157 }
158 
unmap(const gl::Context * context,GLboolean * result)159 angle::Result BufferWgpu::unmap(const gl::Context *context, GLboolean *result)
160 {
161     *result = GL_TRUE;
162     return angle::Result::Continue;
163 }
164 
getIndexRange(const gl::Context * context,gl::DrawElementsType type,size_t offset,size_t count,bool primitiveRestartEnabled,gl::IndexRange * outRange)165 angle::Result BufferWgpu::getIndexRange(const gl::Context *context,
166                                         gl::DrawElementsType type,
167                                         size_t offset,
168                                         size_t count,
169                                         bool primitiveRestartEnabled,
170                                         gl::IndexRange *outRange)
171 {
172     ContextWgpu *contextWgpu = webgpu::GetImpl(context);
173     wgpu::Device device      = webgpu::GetDevice(context);
174 
175     if (mBuffer.getMappedState())
176     {
177         ANGLE_TRY(mBuffer.unmap());
178     }
179 
180     // Create a staging buffer just big enough for this index range
181     const GLuint typeBytes = gl::GetDrawElementsTypeSize(type);
182     const size_t stagingBufferSize =
183         roundUpPow2(count * typeBytes, webgpu::kBufferCopyToBufferAlignment);
184 
185     webgpu::BufferHelper stagingBuffer;
186     ANGLE_TRY(stagingBuffer.initBuffer(device, stagingBufferSize,
187                                        wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead,
188                                        webgpu::MapAtCreation::No));
189 
190     // Copy the source buffer to staging and flush the commands
191     contextWgpu->ensureCommandEncoderCreated();
192     wgpu::CommandEncoder &commandEncoder = contextWgpu->getCurrentCommandEncoder();
193     commandEncoder.CopyBufferToBuffer(mBuffer.getBuffer(), offset, stagingBuffer.getBuffer(), 0,
194                                       stagingBufferSize);
195 
196     ANGLE_TRY(contextWgpu->flush(webgpu::RenderPassClosureReason::IndexRangeReadback));
197 
198     // Read back from the staging buffer and compute the index range
199     ANGLE_TRY(stagingBuffer.mapImmediate(contextWgpu, wgpu::MapMode::Read, 0, stagingBufferSize));
200     const uint8_t *data = stagingBuffer.getMapReadPointer(0, stagingBufferSize);
201     *outRange           = gl::ComputeIndexRange(type, data, count, primitiveRestartEnabled);
202     ANGLE_TRY(stagingBuffer.unmap());
203 
204     return angle::Result::Continue;
205 }
206 
207 }  // namespace rx
208