• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&copyTexture, &copyBuffer, &copySize);
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