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