Compare commits
3 Commits
validation
...
ebridgewat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b6496d352 | ||
|
|
06ffa35540 | ||
|
|
b6ed1313c6 |
@@ -1079,7 +1079,7 @@ void WebGPUDriver::commit(Handle<HwSwapChain> sch) {
|
|||||||
mCommandBuffer = nullptr;
|
mCommandBuffer = nullptr;
|
||||||
mTextureView = nullptr;
|
mTextureView = nullptr;
|
||||||
assert_invariant(mSwapChain);
|
assert_invariant(mSwapChain);
|
||||||
mSwapChain->present();
|
mSwapChain->present(mQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
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,
|
void WebGPUDriver::readPixels(Handle<HwRenderTarget> sourceRenderTargetHandle, const uint32_t x,
|
||||||
const uint32_t y, const uint32_t width, const uint32_t height,
|
const uint32_t y, const uint32_t width, const uint32_t height,
|
||||||
PixelBufferDescriptor&& pixelBufferDescriptor) {
|
PixelBufferDescriptor&& pixelBufferDescriptor) {
|
||||||
// todo
|
auto srcTarget = handleCast<WebGPURenderTarget>(sourceRenderTargetHandle);
|
||||||
scheduleDestroy(std::move(pixelBufferDescriptor));
|
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, ©Size);
|
||||||
|
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,
|
void WebGPUDriver::readBufferSubData(Handle<HwBufferObject> bufferObjectHandle,
|
||||||
|
|||||||
@@ -377,8 +377,24 @@ wgpu::TextureView WebGPUSwapChain::getCurrentHeadlessTextureView() {
|
|||||||
return mRenderTargetViews[mHeadlessBufferIndex];
|
return mRenderTargetViews[mHeadlessBufferIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUSwapChain::present() {
|
wgpu::Texture WebGPUSwapChain::getCurrentTexture(wgpu::Extent2D const& extent) {
|
||||||
if (isHeadless()) {
|
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;
|
mHeadlessBufferIndex = (mHeadlessBufferIndex + 1) % mHeadlessBufferCount;
|
||||||
} else {
|
} else {
|
||||||
mSurface.Present();
|
mSurface.Present();
|
||||||
|
|||||||
@@ -42,12 +42,13 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] wgpu::TextureView getCurrentTextureView(wgpu::Extent2D const& extent);
|
[[nodiscard]] wgpu::TextureView getCurrentTextureView(wgpu::Extent2D const& extent);
|
||||||
[[nodiscard]] wgpu::TextureView getCurrentHeadlessTextureView();
|
[[nodiscard]] wgpu::TextureView getCurrentHeadlessTextureView();
|
||||||
|
[[nodiscard]] wgpu::Texture getCurrentTexture(wgpu::Extent2D const& extent);
|
||||||
|
|
||||||
[[nodiscard]] wgpu::TextureView getDepthTextureView() const { return mDepthTextureView; }
|
[[nodiscard]] wgpu::TextureView getDepthTextureView() const { return mDepthTextureView; }
|
||||||
|
|
||||||
[[nodiscard]] bool isHeadless() const { return mType == SwapChainType::HEADLESS; }
|
[[nodiscard]] bool isHeadless() const { return mType == SwapChainType::HEADLESS; }
|
||||||
|
|
||||||
void present();
|
void present(wgpu::Queue const& queue);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user