Compare commits

...

3 Commits

Author SHA1 Message Date
bridgewaterrobbie
3b6496d352 Implement readpixels initial 2025-07-23 11:34:29 -04:00
bridgewaterrobbie
06ffa35540 f1 2025-07-22 14:00:06 -04:00
bridgewaterrobbie
b6ed1313c6 tweak1 2025-07-22 13:56:45 -04:00
3 changed files with 98 additions and 5 deletions

View File

@@ -1079,7 +1079,7 @@ void WebGPUDriver::commit(Handle<HwSwapChain> sch) {
mCommandBuffer = nullptr;
mTextureView = nullptr;
assert_invariant(mSwapChain);
mSwapChain->present();
mSwapChain->present(mQueue);
}
void WebGPUDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
@@ -1110,8 +1110,84 @@ void WebGPUDriver::stopCapture(int) {
void WebGPUDriver::readPixels(Handle<HwRenderTarget> sourceRenderTargetHandle, const uint32_t x,
const uint32_t y, const uint32_t width, const uint32_t height,
PixelBufferDescriptor&& pixelBufferDescriptor) {
// todo
scheduleDestroy(std::move(pixelBufferDescriptor));
auto srcTarget = handleCast<WebGPURenderTarget>(sourceRenderTargetHandle);
assert_invariant(srcTarget);
wgpu::Texture srcTexture = nullptr;
if (srcTarget->isDefaultRenderTarget()) {
assert_invariant(mSwapChain);
srcTexture = mSwapChain->getCurrentTexture(mPlatform.getSurfaceExtent(mNativeWindow));
} else {
// TODO: Handle custom render targets
scheduleDestroy(std::move(pixelBufferDescriptor));
return;
}
assert_invariant(srcTexture);
// Create a staging buffer to copy the texture to
const size_t bytesPerPixel = 4;
const size_t unpaddedBytesPerRow = width * bytesPerPixel;
const size_t alignment = 256;
const size_t paddedBytesPerRow = (unpaddedBytesPerRow + alignment - 1) & ~(alignment - 1);
size_t bufferSize = paddedBytesPerRow * height;
wgpu::BufferDescriptor bufferDesc;
bufferDesc.size = bufferSize;
bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead;
wgpu::Buffer stagingBuffer = mDevice.CreateBuffer(&bufferDesc);
assert_invariant(stagingBuffer);
wgpu::CommandEncoder encoder = mDevice.CreateCommandEncoder();
wgpu::TexelCopyTextureInfo source {
.texture = srcTexture,
.mipLevel = 0,
.origin = { x, y, 0 }
};
wgpu::TexelCopyBufferInfo destination {
.layout = {
.offset = 0,
.bytesPerRow = static_cast<uint32_t>(paddedBytesPerRow),
.rowsPerImage = height,
},
.buffer = stagingBuffer
};
wgpu::Extent3D copySize {
.width = width,
.height = height,
.depthOrArrayLayers = 1
};
encoder.CopyTextureToBuffer(&source, &destination, &copySize);
wgpu::CommandBuffer commandBuffer = encoder.Finish();
mQueue.Submit(1, &commandBuffer);
// Map the buffer to read the data
struct UserData {
PixelBufferDescriptor pbd;
wgpu::Buffer buffer;
size_t unpaddedBytesPerRow;
size_t paddedBytesPerRow;
uint32_t height;
};
auto userData = std::make_unique<UserData>();
userData->pbd = std::move(pixelBufferDescriptor);
userData->buffer = stagingBuffer;
userData->unpaddedBytesPerRow = unpaddedBytesPerRow;
userData->paddedBytesPerRow = paddedBytesPerRow;
userData->height = height;
stagingBuffer.MapAsync(wgpu::MapMode::Read, 0, bufferSize, wgpu::CallbackMode::AllowSpontaneous, [](wgpu::MapAsyncStatus status, const char* message, UserData* userdata) {
std::unique_ptr<UserData> data(static_cast<UserData*>(userdata));
if (status == wgpu::MapAsyncStatus::Success) {
const char* src = static_cast<const char*>(data->buffer.GetConstMappedRange(0, data->buffer.GetSize()));
char* dst = static_cast<char*>(data->pbd.buffer);
for (uint32_t i = 0; i < data->height; ++i) {
memcpy(dst + i * data->unpaddedBytesPerRow, src + i * data->paddedBytesPerRow, data->unpaddedBytesPerRow);
}
data->buffer.Unmap();
}
// scheduleDestroy(std::move(data->pbd)); // This line is problematic, need to pass scheduleDestroy func
}, userData.release());
}
void WebGPUDriver::readBufferSubData(Handle<HwBufferObject> bufferObjectHandle,

View File

@@ -377,8 +377,24 @@ wgpu::TextureView WebGPUSwapChain::getCurrentHeadlessTextureView() {
return mRenderTargetViews[mHeadlessBufferIndex];
}
void WebGPUSwapChain::present() {
wgpu::Texture WebGPUSwapChain::getCurrentTexture(wgpu::Extent2D const& extent) {
if (isHeadless()) {
return mRenderTargetTextures[mHeadlessBufferIndex];
} else {
setExtent(extent);
wgpu::SurfaceTexture surfaceTexture;
mSurface.GetCurrentTexture(&surfaceTexture);
return surfaceTexture.texture;
}
}
void WebGPUSwapChain::present(wgpu::Queue const& queue) {
if (isHeadless()) {
// To ensure the CPU doesn't read the texture before the GPU is done,
// we wait for the queue to be idle.
wgpu::Future future = queue.OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly, [](wgpu::QueueWorkDoneStatus) {});
wgpu::FutureWaitInfo waitInfo { .future = future };
mDevice.GetAdapter().GetInstance().WaitAny(1, &waitInfo, UINT64_MAX);
mHeadlessBufferIndex = (mHeadlessBufferIndex + 1) % mHeadlessBufferCount;
} else {
mSurface.Present();

View File

@@ -42,12 +42,13 @@ public:
[[nodiscard]] wgpu::TextureView getCurrentTextureView(wgpu::Extent2D const& extent);
[[nodiscard]] wgpu::TextureView getCurrentHeadlessTextureView();
[[nodiscard]] wgpu::Texture getCurrentTexture(wgpu::Extent2D const& extent);
[[nodiscard]] wgpu::TextureView getDepthTextureView() const { return mDepthTextureView; }
[[nodiscard]] bool isHeadless() const { return mType == SwapChainType::HEADLESS; }
void present();
void present(wgpu::Queue const& queue);
private: