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
7 #include "libANGLE/renderer/wgpu/wgpu_helpers.h"
8 #include "libANGLE/formatutils.h"
9
10 #include "libANGLE/renderer/wgpu/ContextWgpu.h"
11 #include "libANGLE/renderer/wgpu/DisplayWgpu.h"
12 #include "libANGLE/renderer/wgpu/FramebufferWgpu.h"
13 #include "wgpu_helpers.h"
14
15 namespace rx
16 {
17 namespace webgpu
18 {
ImageHelper()19 ImageHelper::ImageHelper()
20 {
21 // TODO: support more TextureFormats.
22 mViewFormats.push_back(wgpu::TextureFormat::RGBA8Unorm);
23 }
24
~ImageHelper()25 ImageHelper::~ImageHelper() {}
26
initImage(wgpu::Device & device,gl::LevelIndex firstAllocatedLevel,wgpu::TextureDescriptor textureDescriptor)27 angle::Result ImageHelper::initImage(wgpu::Device &device,
28 gl::LevelIndex firstAllocatedLevel,
29 wgpu::TextureDescriptor textureDescriptor)
30 {
31 mTextureDescriptor = textureDescriptor;
32 mFirstAllocatedLevel = firstAllocatedLevel;
33 mTexture = device.CreateTexture(&mTextureDescriptor);
34 mInitialized = true;
35
36 return angle::Result::Continue;
37 }
38
flushStagedUpdates(ContextWgpu * contextWgpu,ClearValuesArray * deferredClears,uint32_t deferredClearIndex)39 angle::Result ImageHelper::flushStagedUpdates(ContextWgpu *contextWgpu,
40 ClearValuesArray *deferredClears,
41 uint32_t deferredClearIndex)
42 {
43 if (mSubresourceQueue.empty())
44 {
45 return angle::Result::Continue;
46 }
47 wgpu::Device device = contextWgpu->getDevice();
48 wgpu::Queue queue = contextWgpu->getQueue();
49 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
50 wgpu::ImageCopyTexture dst;
51 dst.texture = mTexture;
52 std::vector<wgpu::RenderPassColorAttachment> colorAttachments;
53 for (const SubresourceUpdate &srcUpdate : mSubresourceQueue)
54 {
55 if (!isTextureLevelInAllocatedImage(srcUpdate.targetLevel))
56 {
57 continue;
58 }
59 switch (srcUpdate.updateSource)
60 {
61 case UpdateSource::Texture:
62 dst.mipLevel = toWgpuLevel(srcUpdate.targetLevel).get();
63 encoder.CopyBufferToTexture(&srcUpdate.textureData, &dst, &mTextureDescriptor.size);
64 break;
65 case UpdateSource::Clear:
66 if (deferredClears)
67 {
68 deferredClears->store(deferredClearIndex, srcUpdate.clearData);
69 }
70 else
71 {
72
73 wgpu::TextureView textureView;
74 ANGLE_TRY(createTextureView(srcUpdate.targetLevel, 0, textureView));
75
76 colorAttachments.push_back(
77 CreateNewClearColorAttachment(srcUpdate.clearData.clearColor,
78 srcUpdate.clearData.depthSlice, textureView));
79 }
80 break;
81 }
82 }
83
84 if (!colorAttachments.empty())
85 {
86 FramebufferWgpu *frameBuffer =
87 GetImplAs<FramebufferWgpu>(contextWgpu->getState().getDrawFramebuffer());
88 frameBuffer->addNewColorAttachments(colorAttachments);
89 }
90 wgpu::CommandBuffer commandBuffer = encoder.Finish();
91 queue.Submit(1, &commandBuffer);
92 encoder = nullptr;
93 mSubresourceQueue.clear();
94
95 return angle::Result::Continue;
96 }
97
createTextureDescriptor(wgpu::TextureUsage usage,wgpu::TextureDimension dimension,wgpu::Extent3D size,wgpu::TextureFormat format,std::uint32_t mipLevelCount,std::uint32_t sampleCount)98 wgpu::TextureDescriptor ImageHelper::createTextureDescriptor(wgpu::TextureUsage usage,
99 wgpu::TextureDimension dimension,
100 wgpu::Extent3D size,
101 wgpu::TextureFormat format,
102 std::uint32_t mipLevelCount,
103 std::uint32_t sampleCount)
104 {
105 wgpu::TextureDescriptor textureDescriptor = {};
106 textureDescriptor.usage = usage;
107 textureDescriptor.dimension = dimension;
108 textureDescriptor.size = size;
109 textureDescriptor.format = format;
110 textureDescriptor.mipLevelCount = mipLevelCount;
111 textureDescriptor.sampleCount = sampleCount;
112 textureDescriptor.viewFormatCount = mViewFormats.size();
113 textureDescriptor.viewFormats = reinterpret_cast<wgpu::TextureFormat *>(mViewFormats.data());
114 return textureDescriptor;
115 }
116
stageTextureUpload(ContextWgpu * contextWgpu,const gl::Extents & glExtents,GLuint inputRowPitch,GLuint inputDepthPitch,uint32_t outputRowPitch,uint32_t outputDepthPitch,uint32_t allocationSize,const gl::ImageIndex & index,const uint8_t * pixels)117 angle::Result ImageHelper::stageTextureUpload(ContextWgpu *contextWgpu,
118 const gl::Extents &glExtents,
119 GLuint inputRowPitch,
120 GLuint inputDepthPitch,
121 uint32_t outputRowPitch,
122 uint32_t outputDepthPitch,
123 uint32_t allocationSize,
124 const gl::ImageIndex &index,
125 const uint8_t *pixels)
126 {
127 if (pixels == nullptr)
128 {
129 return angle::Result::Continue;
130 }
131 wgpu::Device device = contextWgpu->getDevice();
132 wgpu::Queue queue = contextWgpu->getQueue();
133 gl::LevelIndex levelGL(index.getLevelIndex());
134 BufferHelper bufferHelper;
135 wgpu::BufferUsage usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
136 ANGLE_TRY(bufferHelper.initBuffer(device, allocationSize, usage, MapAtCreation::Yes));
137 LoadImageFunctionInfo loadFunctionInfo = {angle::LoadToNative<GLubyte, 4>, false};
138 uint8_t *data = bufferHelper.getMapWritePointer(0, allocationSize);
139 loadFunctionInfo.loadFunction(contextWgpu->getImageLoadContext(), glExtents.width,
140 glExtents.height, glExtents.depth, pixels, inputRowPitch,
141 inputDepthPitch, data, outputRowPitch, outputDepthPitch);
142 ANGLE_TRY(bufferHelper.unmap());
143
144 wgpu::TextureDataLayout textureDataLayout = {};
145 textureDataLayout.bytesPerRow = outputRowPitch;
146 textureDataLayout.rowsPerImage = outputDepthPitch;
147 wgpu::ImageCopyBuffer imageCopyBuffer;
148 imageCopyBuffer.layout = textureDataLayout;
149 imageCopyBuffer.buffer = bufferHelper.getBuffer();
150 SubresourceUpdate subresourceUpdate(UpdateSource::Texture, levelGL, imageCopyBuffer);
151 mSubresourceQueue.push_back(subresourceUpdate);
152 return angle::Result::Continue;
153 }
154
stageClear(gl::LevelIndex targetLevel,ClearValues clearValues)155 void ImageHelper::stageClear(gl::LevelIndex targetLevel, ClearValues clearValues)
156 {
157 SubresourceUpdate subresourceUpdate(UpdateSource::Clear, targetLevel, clearValues);
158 mSubresourceQueue.push_back(subresourceUpdate);
159 }
160
removeStagedUpdates(gl::LevelIndex levelToRemove)161 void ImageHelper::removeStagedUpdates(gl::LevelIndex levelToRemove)
162 {
163 for (auto it = mSubresourceQueue.begin(); it != mSubresourceQueue.end(); it++)
164 {
165 if (it->updateSource == UpdateSource::Texture && it->targetLevel == levelToRemove)
166 {
167 mSubresourceQueue.erase(it);
168 }
169 }
170 }
171
resetImage()172 void ImageHelper::resetImage()
173 {
174 mTexture.Destroy();
175 mTextureDescriptor = {};
176 mInitialized = false;
177 mFirstAllocatedLevel = gl::LevelIndex(0);
178 }
179 // static
getReadPixelsParams(rx::ContextWgpu * contextWgpu,const gl::PixelPackState & packState,gl::Buffer * packBuffer,GLenum format,GLenum type,const gl::Rectangle & area,const gl::Rectangle & clippedArea,rx::PackPixelsParams * paramsOut,GLuint * skipBytesOut)180 angle::Result ImageHelper::getReadPixelsParams(rx::ContextWgpu *contextWgpu,
181 const gl::PixelPackState &packState,
182 gl::Buffer *packBuffer,
183 GLenum format,
184 GLenum type,
185 const gl::Rectangle &area,
186 const gl::Rectangle &clippedArea,
187 rx::PackPixelsParams *paramsOut,
188 GLuint *skipBytesOut)
189 {
190 const gl::InternalFormat &sizedFormatInfo = gl::GetInternalFormatInfo(format, type);
191
192 GLuint outputPitch = 0;
193 ANGLE_CHECK_GL_MATH(contextWgpu,
194 sizedFormatInfo.computeRowPitch(type, area.width, packState.alignment,
195 packState.rowLength, &outputPitch));
196 ANGLE_CHECK_GL_MATH(contextWgpu, sizedFormatInfo.computeSkipBytes(
197 type, outputPitch, 0, packState, false, skipBytesOut));
198
199 ANGLE_TRY(GetPackPixelsParams(sizedFormatInfo, outputPitch, packState, packBuffer, area,
200 clippedArea, paramsOut, skipBytesOut));
201 return angle::Result::Continue;
202 }
203
readPixels(rx::ContextWgpu * contextWgpu,const gl::Rectangle & area,const rx::PackPixelsParams & packPixelsParams,const angle::Format & aspectFormat,void * pixels)204 angle::Result ImageHelper::readPixels(rx::ContextWgpu *contextWgpu,
205 const gl::Rectangle &area,
206 const rx::PackPixelsParams &packPixelsParams,
207 const angle::Format &aspectFormat,
208 void *pixels)
209 {
210 wgpu::Device device = contextWgpu->getDisplay()->getDevice();
211 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
212 wgpu::Queue queue = contextWgpu->getDisplay()->getQueue();
213 BufferHelper bufferHelper;
214 uint32_t textureBytesPerRow =
215 roundUp(aspectFormat.pixelBytes * area.width, kCopyBufferAlignment);
216 wgpu::TextureDataLayout textureDataLayout;
217 textureDataLayout.bytesPerRow = textureBytesPerRow;
218 textureDataLayout.rowsPerImage = area.height;
219
220 size_t allocationSize = textureBytesPerRow * area.height;
221
222 ANGLE_TRY(bufferHelper.initBuffer(device, allocationSize,
223 wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst,
224 MapAtCreation::No));
225 wgpu::ImageCopyBuffer copyBuffer;
226 copyBuffer.buffer = bufferHelper.getBuffer();
227 copyBuffer.layout = textureDataLayout;
228
229 wgpu::ImageCopyTexture copyTexture;
230 wgpu::Origin3D textureOrigin;
231 textureOrigin.x = area.x;
232 textureOrigin.y = area.y;
233 copyTexture.origin = textureOrigin;
234 copyTexture.texture = mTexture;
235 copyTexture.mipLevel = toWgpuLevel(mFirstAllocatedLevel).get();
236
237 wgpu::Extent3D copySize;
238 copySize.width = area.width;
239 copySize.height = area.height;
240 encoder.CopyTextureToBuffer(©Texture, ©Buffer, ©Size);
241
242 wgpu::CommandBuffer commandBuffer = encoder.Finish();
243 queue.Submit(1, &commandBuffer);
244 encoder = nullptr;
245
246 ANGLE_TRY(bufferHelper.mapImmediate(contextWgpu, wgpu::MapMode::Read, 0, allocationSize));
247 const uint8_t *readPixelBuffer = bufferHelper.getMapReadPointer(0, allocationSize);
248 PackPixels(packPixelsParams, aspectFormat, textureBytesPerRow, readPixelBuffer,
249 static_cast<uint8_t *>(pixels));
250 return angle::Result::Continue;
251 }
252
createTextureView(gl::LevelIndex targetLevel,uint32_t layerIndex,wgpu::TextureView & textureViewOut)253 angle::Result ImageHelper::createTextureView(gl::LevelIndex targetLevel,
254 uint32_t layerIndex,
255 wgpu::TextureView &textureViewOut)
256 {
257 if (!isTextureLevelInAllocatedImage(targetLevel))
258 {
259 return angle::Result::Stop;
260 }
261 wgpu::TextureViewDescriptor textureViewDesc;
262 textureViewDesc.aspect = wgpu::TextureAspect::All;
263 textureViewDesc.baseArrayLayer = layerIndex;
264 textureViewDesc.arrayLayerCount = 1;
265 textureViewDesc.baseMipLevel = toWgpuLevel(targetLevel).get();
266 textureViewDesc.mipLevelCount = 1;
267 switch (mTextureDescriptor.dimension)
268 {
269 case wgpu::TextureDimension::Undefined:
270 textureViewDesc.dimension = wgpu::TextureViewDimension::Undefined;
271 break;
272 case wgpu::TextureDimension::e1D:
273 textureViewDesc.dimension = wgpu::TextureViewDimension::e1D;
274 break;
275 case wgpu::TextureDimension::e2D:
276 textureViewDesc.dimension = wgpu::TextureViewDimension::e2D;
277 break;
278 case wgpu::TextureDimension::e3D:
279 textureViewDesc.dimension = wgpu::TextureViewDimension::e3D;
280 break;
281 }
282 textureViewDesc.format = mTextureDescriptor.format;
283 textureViewOut = mTexture.CreateView(&textureViewDesc);
284 return angle::Result::Continue;
285 }
286
getLastAllocatedLevel()287 gl::LevelIndex ImageHelper::getLastAllocatedLevel()
288 {
289 return mFirstAllocatedLevel + mTextureDescriptor.mipLevelCount - 1;
290 }
291
toWgpuLevel(gl::LevelIndex levelIndexGl) const292 LevelIndex ImageHelper::toWgpuLevel(gl::LevelIndex levelIndexGl) const
293 {
294 return gl_wgpu::getLevelIndex(levelIndexGl, mFirstAllocatedLevel);
295 }
296
toGlLevel(LevelIndex levelIndexWgpu) const297 gl::LevelIndex ImageHelper::toGlLevel(LevelIndex levelIndexWgpu) const
298 {
299 return wgpu_gl::getLevelIndex(levelIndexWgpu, mFirstAllocatedLevel);
300 }
301
isTextureLevelInAllocatedImage(gl::LevelIndex textureLevel)302 bool ImageHelper::isTextureLevelInAllocatedImage(gl::LevelIndex textureLevel)
303 {
304 if (!mInitialized || textureLevel < mFirstAllocatedLevel)
305 {
306 return false;
307 }
308 LevelIndex wgpuTextureLevel = toWgpuLevel(textureLevel);
309 return wgpuTextureLevel < LevelIndex(mTextureDescriptor.mipLevelCount);
310 }
311
BufferHelper()312 BufferHelper::BufferHelper() {}
313
~BufferHelper()314 BufferHelper::~BufferHelper() {}
315
reset()316 void BufferHelper::reset()
317 {
318 mBuffer = nullptr;
319 mMappedState.reset();
320 }
321
initBuffer(wgpu::Device device,size_t size,wgpu::BufferUsage usage,MapAtCreation mappedAtCreation)322 angle::Result BufferHelper::initBuffer(wgpu::Device device,
323 size_t size,
324 wgpu::BufferUsage usage,
325 MapAtCreation mappedAtCreation)
326 {
327 wgpu::BufferDescriptor descriptor;
328 descriptor.size = size;
329 descriptor.usage = usage;
330 descriptor.mappedAtCreation = mappedAtCreation == MapAtCreation::Yes;
331
332 mBuffer = device.CreateBuffer(&descriptor);
333
334 if (mappedAtCreation == MapAtCreation::Yes)
335 {
336 mMappedState = {wgpu::MapMode::Read | wgpu::MapMode::Write, 0, size};
337 }
338 else
339 {
340 mMappedState.reset();
341 }
342
343 return angle::Result::Continue;
344 }
345
mapImmediate(ContextWgpu * context,wgpu::MapMode mode,size_t offset,size_t size)346 angle::Result BufferHelper::mapImmediate(ContextWgpu *context,
347 wgpu::MapMode mode,
348 size_t offset,
349 size_t size)
350 {
351 ASSERT(!mMappedState.has_value());
352
353 WGPUBufferMapAsyncStatus mapResult = WGPUBufferMapAsyncStatus_Unknown;
354
355 wgpu::BufferMapCallbackInfo callbackInfo;
356 callbackInfo.mode = wgpu::CallbackMode::WaitAnyOnly;
357 callbackInfo.callback = [](WGPUBufferMapAsyncStatus status, void *userdata) {
358 *static_cast<WGPUBufferMapAsyncStatus *>(userdata) = status;
359 };
360 callbackInfo.userdata = &mapResult;
361
362 wgpu::FutureWaitInfo waitInfo;
363 waitInfo.future = mBuffer.MapAsync(mode, offset, size, callbackInfo);
364
365 wgpu::Instance instance = context->getDisplay()->getInstance();
366 ANGLE_WGPU_TRY(context, instance.WaitAny(1, &waitInfo, -1));
367 ANGLE_WGPU_TRY(context, mapResult);
368
369 ASSERT(waitInfo.completed);
370
371 mMappedState = {mode, offset, size};
372
373 return angle::Result::Continue;
374 }
375
unmap()376 angle::Result BufferHelper::unmap()
377 {
378 ASSERT(mMappedState.has_value());
379 mBuffer.Unmap();
380 mMappedState.reset();
381 return angle::Result::Continue;
382 }
383
getMapWritePointer(size_t offset,size_t size) const384 uint8_t *BufferHelper::getMapWritePointer(size_t offset, size_t size) const
385 {
386 ASSERT(mBuffer.GetMapState() == wgpu::BufferMapState::Mapped);
387 ASSERT(mMappedState.has_value());
388 ASSERT(mMappedState->offset <= offset);
389 ASSERT(mMappedState->offset + mMappedState->size >= offset + size);
390
391 void *mapPtr = mBuffer.GetMappedRange(offset, size);
392 ASSERT(mapPtr);
393
394 return static_cast<uint8_t *>(mapPtr);
395 }
396
getMapReadPointer(size_t offset,size_t size) const397 const uint8_t *BufferHelper::getMapReadPointer(size_t offset, size_t size) const
398 {
399 ASSERT(mBuffer.GetMapState() == wgpu::BufferMapState::Mapped);
400 ASSERT(mMappedState.has_value());
401 ASSERT(mMappedState->offset <= offset);
402 ASSERT(mMappedState->offset + mMappedState->size >= offset + size);
403
404 // GetConstMappedRange is used for reads whereas GetMappedRange is only used for writes.
405 const void *mapPtr = mBuffer.GetConstMappedRange(offset, size);
406 ASSERT(mapPtr);
407
408 return static_cast<const uint8_t *>(mapPtr);
409 }
410
getMappedState() const411 const std::optional<BufferMapState> &BufferHelper::getMappedState() const
412 {
413 return mMappedState;
414 }
415
canMapForRead() const416 bool BufferHelper::canMapForRead() const
417 {
418 return (mMappedState.has_value() && (mMappedState->mode & wgpu::MapMode::Read)) ||
419 (mBuffer && (mBuffer.GetUsage() & wgpu::BufferUsage::MapRead));
420 }
421
canMapForWrite() const422 bool BufferHelper::canMapForWrite() const
423 {
424 return (mMappedState.has_value() && (mMappedState->mode & wgpu::MapMode::Write)) ||
425 (mBuffer && (mBuffer.GetUsage() & wgpu::BufferUsage::MapWrite));
426 }
427
getBuffer()428 wgpu::Buffer &BufferHelper::getBuffer()
429 {
430 return mBuffer;
431 }
432
size() const433 uint64_t BufferHelper::size() const
434 {
435 return mBuffer ? mBuffer.GetSize() : 0;
436 }
437 } // namespace webgpu
438 } // namespace rx
439