// Copyright (c) Wojciech Figat. All rights reserved. #if GRAPHICS_API_WEBGPU #include "GPUContextWebGPU.h" #include "GPUShaderWebGPU.h" #include "GPUShaderProgramWebGPU.h" #include "GPUPipelineStateWebGPU.h" #include "GPUTextureWebGPU.h" #include "GPUBufferWebGPU.h" #include "GPUSamplerWebGPU.h" #include "GPUVertexLayoutWebGPU.h" #include "RenderToolsWebGPU.h" #include "Engine/Core/Math/Viewport.h" #include "Engine/Core/Math/Rectangle.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/RenderStats.h" #include "Engine/Graphics/PixelFormatExtensions.h" // Ensure to match the indirect commands arguments layout static_assert(sizeof(GPUDispatchIndirectArgs) == sizeof(uint32) * 3, "Wrong size of GPUDrawIndirectArgs."); static_assert(OFFSET_OF(GPUDispatchIndirectArgs, ThreadGroupCountX) == sizeof(uint32) * 0, "Wrong offset for GPUDrawIndirectArgs::ThreadGroupCountX"); static_assert(OFFSET_OF(GPUDispatchIndirectArgs, ThreadGroupCountY) == sizeof(uint32) * 1, "Wrong offset for GPUDrawIndirectArgs::ThreadGroupCountY"); static_assert(OFFSET_OF(GPUDispatchIndirectArgs, ThreadGroupCountZ) == sizeof(uint32) * 2, "Wrong offset for GPUDrawIndirectArgs::ThreadGroupCountZ"); // static_assert(sizeof(GPUDrawIndirectArgs) == sizeof(uint32) * 4, "Wrong size of GPUDrawIndirectArgs."); static_assert(OFFSET_OF(GPUDrawIndirectArgs, VerticesCount) == sizeof(uint32) * 0, "Wrong offset for GPUDrawIndirectArgs::VerticesCount"); static_assert(OFFSET_OF(GPUDrawIndirectArgs, InstanceCount) == sizeof(uint32) * 1, "Wrong offset for GPUDrawIndirectArgs::InstanceCount"); static_assert(OFFSET_OF(GPUDrawIndirectArgs, StartVertex) == sizeof(uint32) * 2, "Wrong offset for GPUDrawIndirectArgs::StartVertex"); static_assert(OFFSET_OF(GPUDrawIndirectArgs, StartInstance) == sizeof(uint32) * 3, "Wrong offset for GPUDrawIndirectArgs::StartInstance"); // static_assert(sizeof(GPUDrawIndexedIndirectArgs) == sizeof(uint32) * 5, "Wrong size of GPUDrawIndexedIndirectArgs."); static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, IndicesCount) == sizeof(uint32) * 0, "Wrong offset for GPUDrawIndexedIndirectArgs::IndicesCount"); static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, InstanceCount) == sizeof(uint32) * 1, "Wrong offset for GPUDrawIndexedIndirectArgs::InstanceCount"); static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, StartIndex) == sizeof(uint32) * 2, "Wrong offset for GPUDrawIndexedIndirectArgs::StartIndex"); static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, StartVertex) == sizeof(uint32) * 3, "Wrong offset for GPUDrawIndexedIndirectArgs::StartVertex"); static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, StartInstance) == sizeof(uint32) * 4, "Wrong offset for GPUDrawIndexedIndirectArgs::StartInstance"); GPUContextWebGPU::GPUContextWebGPU(GPUDeviceWebGPU* device) : GPUContext(device) , _device(device) { _vertexBufferNullLayout = WGPU_VERTEX_BUFFER_LAYOUT_INIT; } GPUContextWebGPU::~GPUContextWebGPU() { CHECK(Encoder == nullptr); } void GPUContextWebGPU::FrameBegin() { // Base GPUContext::FrameBegin(); // Setup _renderPassDirty = false; _pipelineDirty = false; _bindGroupDirty = false; _indexBufferDirty = false; _vertexBufferDirty = false; _indexBuffer32Bit = false; _blendFactorDirty = false; _blendFactorSet = false; _renderTargetCount = 0; _vertexBufferCount = 0; _stencilRef = 0; _blendFactor = Float4::One; _viewport = Viewport(Float2::Zero); _scissorRect = Rectangle::Empty; _renderPass = nullptr; _depthStencil = nullptr; _pipelineState = nullptr; Platform::MemoryClear(&_pipelineKey, sizeof(_pipelineKey)); Platform::MemoryClear(&_indexBuffer, sizeof(_indexBuffer)); Platform::MemoryClear(&_vertexBuffers, sizeof(_vertexBuffers)); Platform::MemoryClear(&_renderTargets, sizeof(_renderTargets)); Platform::MemoryClear(&_constantBuffers, sizeof(_constantBuffers)); Platform::MemoryClear(&_shaderResources, sizeof(_shaderResources)); Platform::MemoryClear(&_storageResources, sizeof(_storageResources)); _pendingClears.Clear(); // Create command encoder WGPUCommandEncoderDescriptor encoderDesc = WGPU_COMMAND_ENCODER_DESCRIPTOR_INIT; Encoder = wgpuDeviceCreateCommandEncoder(_device->Device, &encoderDesc); ASSERT(Encoder); // Bind static samplers for (int32 i = 0; i < ARRAY_COUNT(_device->DefaultSamplers); i++) _samplers[i] = _device->DefaultSamplers[i]; } void GPUContextWebGPU::FrameEnd() { // Base GPUContext::FrameEnd(); // Flush command encoder to the command buffer and submit them on a queue Flush(); } void* GPUContextWebGPU::GetNativePtr() const { return Encoder; } bool GPUContextWebGPU::IsDepthBufferBinded() { return _depthStencil != nullptr; } void GPUContextWebGPU::Clear(GPUTextureView* rt, const Color& color) { auto& clear = _pendingClears.AddOne(); clear.View = (GPUTextureViewWebGPU*)rt; Platform::MemoryCopy(clear.RGBA, color.Raw, sizeof(color.Raw)); } void GPUContextWebGPU::ClearDepth(GPUTextureView* depthBuffer, float depthValue, uint8 stencilValue) { auto& clear = _pendingClears.AddOne(); clear.View = (GPUTextureViewWebGPU*)depthBuffer; clear.Depth = depthValue; clear.Stencil = stencilValue; } void GPUContextWebGPU::ClearUA(GPUBuffer* buf, const Float4& value) { MISSING_CODE("GPUContextWebGPU::ClearUA"); } void GPUContextWebGPU::ClearUA(GPUBuffer* buf, const uint32 value[4]) { MISSING_CODE("GPUContextWebGPU::ClearUA"); } void GPUContextWebGPU::ClearUA(GPUTexture* texture, const uint32 value[4]) { MISSING_CODE("GPUContextWebGPU::ClearUA"); } void GPUContextWebGPU::ClearUA(GPUTexture* texture, const Float4& value) { MISSING_CODE("GPUContextWebGPU::ClearUA"); } void GPUContextWebGPU::ResetRenderTarget() { if (_renderTargetCount != 0 || _depthStencil) { _renderPassDirty = true; _renderTargetCount = 0; _depthStencil = nullptr; } } void GPUContextWebGPU::SetRenderTarget(GPUTextureView* rt) { auto rtWebGPU = (GPUTextureViewWebGPU*)rt; int32 newRtCount = rtWebGPU ? 1 : 0; if (_renderTargetCount != newRtCount || _renderTargets[0] != rtWebGPU || _depthStencil != nullptr) { _renderPassDirty = true; _renderTargetCount = newRtCount; _depthStencil = nullptr; _renderTargets[0] = rtWebGPU; } } void GPUContextWebGPU::SetRenderTarget(GPUTextureView* depthBuffer, GPUTextureView* rt) { auto depthBufferGPU = (GPUTextureViewWebGPU*)depthBuffer; auto rtWebGPU = (GPUTextureViewWebGPU*)rt; int32 newRtCount = rtWebGPU ? 1 : 0; if (_renderTargetCount != newRtCount || _renderTargets[0] != rtWebGPU || _depthStencil != depthBufferGPU) { _renderPassDirty = true; _renderTargetCount = newRtCount; _depthStencil = depthBufferGPU; _renderTargets[0] = rtWebGPU; } } void GPUContextWebGPU::SetRenderTarget(GPUTextureView* depthBuffer, const Span& rts) { ASSERT(Math::IsInRange(rts.Length(), 1, GPU_MAX_RT_BINDED)); auto depthBufferGPU = (GPUTextureViewWebGPU*)depthBuffer; if (_renderTargetCount != rts.Length() || _depthStencil != depthBufferGPU || Platform::MemoryCompare(_renderTargets, rts.Get(), rts.Length() * sizeof(void*)) != 0) { _renderPassDirty = true; _renderTargetCount = rts.Length(); _depthStencil = depthBufferGPU; Platform::MemoryCopy(_renderTargets, rts.Get(), rts.Length() * sizeof(void*)); } } void GPUContextWebGPU::SetBlendFactor(const Float4& value) { if (_blendFactor != value) { _blendFactorDirty = true; _blendFactor = value; _blendFactorSet = value != Float4::One; } } void GPUContextWebGPU::SetStencilRef(uint32 value) { if (_stencilRef != value) { _stencilRef = value; if (_renderPass) wgpuRenderPassEncoderSetStencilReference(_renderPass, value); } } void GPUContextWebGPU::ResetSR() { Platform::MemoryClear(_shaderResources, sizeof(_shaderResources)); } void GPUContextWebGPU::ResetUA() { Platform::MemoryClear(_storageResources, sizeof(_storageResources)); } void GPUContextWebGPU::ResetCB() { _bindGroupDirty = false; Platform::MemoryClear(_constantBuffers, sizeof(_constantBuffers)); } void GPUContextWebGPU::BindCB(int32 slot, GPUConstantBuffer* cb) { ASSERT(slot >= 0 && slot < GPU_MAX_CB_BINDED); auto cbWebGPU = (GPUConstantBufferWebGPU*)cb; if (_constantBuffers[slot] != cbWebGPU) { _bindGroupDirty = true; _constantBuffers[slot] = cbWebGPU; } } void GPUContextWebGPU::BindSR(int32 slot, GPUResourceView* view) { ASSERT(slot >= 0 && slot < GPU_MAX_SR_BINDED); if (_shaderResources[slot] != view) { _bindGroupDirty = true; _shaderResources[slot] = view; if (view) *view->LastRenderTime = _lastRenderTime; } } void GPUContextWebGPU::BindUA(int32 slot, GPUResourceView* view) { ASSERT(slot >= 0 && slot < GPU_MAX_UA_BINDED); if (_storageResources[slot] != view) { _bindGroupDirty = true; _storageResources[slot] = view; if (view) *view->LastRenderTime = _lastRenderTime; } } void GPUContextWebGPU::BindVB(const Span& vertexBuffers, const uint32* vertexBuffersOffsets, GPUVertexLayout* vertexLayout) { ASSERT(vertexBuffers.Length() <= GPU_MAX_VB_BINDED); _vertexBufferDirty = true; _vertexBufferCount = vertexBuffers.Length(); _pipelineKey.VertexBufferCount = vertexBuffers.Length(); for (int32 i = 0; i < vertexBuffers.Length(); i++) { auto vbWebGPU = (GPUBufferWebGPU*)vertexBuffers.Get()[i]; _vertexBuffers[i].Buffer = vbWebGPU ? vbWebGPU->Buffer : nullptr; _vertexBuffers[i].Offset = vertexBuffersOffsets ? vertexBuffersOffsets[i] : 0; _vertexBuffers[i].Size = vbWebGPU ? vbWebGPU->GetSize() : 0; _pipelineKey.VertexBuffers[i] = vbWebGPU && vbWebGPU->GetVertexLayout() ? &((GPUVertexLayoutWebGPU*)vbWebGPU->GetVertexLayout())->Layout : &_vertexBufferNullLayout; } } void GPUContextWebGPU::BindIB(GPUBuffer* indexBuffer) { auto ibWebGPU = (GPUBufferWebGPU*)indexBuffer; _indexBufferDirty = true; _indexBuffer32Bit = indexBuffer->GetFormat() == PixelFormat::R32_UInt; _indexBuffer.Buffer = ibWebGPU->Buffer; _indexBuffer.Offset = 0; _indexBuffer.Size = indexBuffer->GetSize(); } void GPUContextWebGPU::BindSampler(int32 slot, GPUSampler* sampler) { ASSERT(slot >= 0 && slot < GPU_MAX_SAMPLER_BINDED); auto samplerWebGPU = (GPUSamplerWebGPU*)sampler; if (_samplers[slot] != samplerWebGPU) { _bindGroupDirty = true; _samplers[slot] = samplerWebGPU; } } void GPUContextWebGPU::UpdateCB(GPUConstantBuffer* cb, const void* data) { ASSERT(data && cb); auto cbWebGPU = static_cast(cb); const uint32 size = cbWebGPU->GetSize(); if (size != 0) { wgpuQueueWriteBuffer(_device->Queue, cbWebGPU->Buffer, 0, data, size); } } void GPUContextWebGPU::Dispatch(GPUShaderProgramCS* shader, uint32 threadGroupCountX, uint32 threadGroupCountY, uint32 threadGroupCountZ) { OnDispatch(shader); MISSING_CODE("GPUContextWebGPU::Dispatch"); RENDER_STAT_DISPATCH_CALL(); } void GPUContextWebGPU::DispatchIndirect(GPUShaderProgramCS* shader, GPUBuffer* bufferForArgs, uint32 offsetForArgs) { ASSERT(bufferForArgs && EnumHasAnyFlags(bufferForArgs->GetFlags(), GPUBufferFlags::Argument)); auto bufferForArgsWebGPU = (GPUBufferWebGPU*)bufferForArgs; OnDispatch(shader); MISSING_CODE("GPUContextWebGPU::Dispatch"); RENDER_STAT_DISPATCH_CALL(); } void GPUContextWebGPU::ResolveMultisample(GPUTexture* sourceMultisampleTexture, GPUTexture* destTexture, int32 sourceSubResource, int32 destSubResource, PixelFormat format) { ASSERT(sourceMultisampleTexture && sourceMultisampleTexture->IsMultiSample()); ASSERT(destTexture && !destTexture->IsMultiSample()); // TODO: do it via a render pass (see WGPURenderPassColorAttachment::resolveTarget) MISSING_CODE("GPUContextWebGPU::ResolveMultisample"); } void GPUContextWebGPU::DrawInstanced(uint32 verticesCount, uint32 instanceCount, int32 startInstance, int32 startVertex) { OnDrawCall(); wgpuRenderPassEncoderDraw(_renderPass, verticesCount, instanceCount, startVertex, startInstance); RENDER_STAT_DRAW_CALL(verticesCount * instanceCount, verticesCount * instanceCount / 3); } void GPUContextWebGPU::DrawIndexedInstanced(uint32 indicesCount, uint32 instanceCount, int32 startInstance, int32 startVertex, int32 startIndex) { OnDrawCall(); wgpuRenderPassEncoderDrawIndexed(_renderPass, indicesCount, instanceCount, startIndex, startVertex, startInstance); RENDER_STAT_DRAW_CALL(indicesCount * instanceCount, indicesCount / 3 * instanceCount); } void GPUContextWebGPU::DrawInstancedIndirect(GPUBuffer* bufferForArgs, uint32 offsetForArgs) { ASSERT(bufferForArgs && EnumHasAnyFlags(bufferForArgs->GetFlags(), GPUBufferFlags::Argument)); const auto bufferForArgsWebGPU = static_cast(bufferForArgs); OnDrawCall(); wgpuRenderPassEncoderDrawIndirect(_renderPass, bufferForArgsWebGPU->Buffer, offsetForArgs); RENDER_STAT_DRAW_CALL(0, 0); } void GPUContextWebGPU::DrawIndexedInstancedIndirect(GPUBuffer* bufferForArgs, uint32 offsetForArgs) { ASSERT(bufferForArgs && EnumHasAnyFlags(bufferForArgs->GetFlags(), GPUBufferFlags::Argument)); const auto bufferForArgsWebGPU = static_cast(bufferForArgs); OnDrawCall(); wgpuRenderPassEncoderDrawIndexedIndirect(_renderPass, bufferForArgsWebGPU->Buffer, offsetForArgs); RENDER_STAT_DRAW_CALL(0, 0); } uint64 GPUContextWebGPU::BeginQuery(GPUQueryType type) { // TODO: impl timer/occlusion queries return 0; } void GPUContextWebGPU::EndQuery(uint64 queryID) { } void GPUContextWebGPU::SetViewport(const Viewport& viewport) { _viewport = viewport; if (_renderPass) wgpuRenderPassEncoderSetViewport(_renderPass, viewport.X, viewport.Y, viewport.Width, viewport.Height, viewport.MinDepth, viewport.MaxDepth); } void GPUContextWebGPU::SetScissor(const Rectangle& scissorRect) { _scissorRect = scissorRect; if (_renderPass) wgpuRenderPassEncoderSetScissorRect(_renderPass, (uint32_t)scissorRect.GetX(), (uint32_t)scissorRect.GetY(), (uint32_t)scissorRect.GetWidth(), (uint32_t)scissorRect.GetHeight()); } void GPUContextWebGPU::SetDepthBounds(float minDepth, float maxDepth) { } GPUPipelineState* GPUContextWebGPU::GetState() const { return _pipelineState; } void GPUContextWebGPU::SetState(GPUPipelineState* state) { if (_pipelineState != state) { _pipelineState = (GPUPipelineStateWebGPU*)state; _pipelineDirty = true; } } void GPUContextWebGPU::ResetState() { if (!Encoder) return; ResetRenderTarget(); ResetSR(); ResetUA(); ResetCB(); SetState(nullptr); FlushState(); } void GPUContextWebGPU::FlushState() { // Flush pending clears for (auto& clear : _pendingClears) ManualClear(clear); _pendingClears.Clear(); } void GPUContextWebGPU::Flush() { if (!Encoder) return; PROFILE_CPU(); WGPUCommandBufferDescriptor commandBufferDesc = WGPU_COMMAND_BUFFER_DESCRIPTOR_INIT; WGPUCommandBuffer commandBuffer = wgpuCommandEncoderFinish(Encoder, &commandBufferDesc); wgpuCommandEncoderRelease(Encoder); if (commandBuffer) { wgpuQueueSubmit(_device->Queue, 1, &commandBuffer); wgpuCommandBufferRelease(commandBuffer); } } void GPUContextWebGPU::UpdateBuffer(GPUBuffer* buffer, const void* data, uint32 size, uint32 offset) { ASSERT(data); ASSERT(buffer && buffer->GetSize() >= size); auto bufferWebGPU = (GPUBufferWebGPU*)buffer; wgpuQueueWriteBuffer(_device->Queue, bufferWebGPU->Buffer, offset, data, size); } void GPUContextWebGPU::CopyBuffer(GPUBuffer* dstBuffer, GPUBuffer* srcBuffer, uint32 size, uint32 dstOffset, uint32 srcOffset) { ASSERT(dstBuffer && srcBuffer); auto srcBufferWebGPU = (GPUBufferWebGPU*)srcBuffer; auto dstBufferWebGPU = (GPUBufferWebGPU*)dstBuffer; wgpuCommandEncoderCopyBufferToBuffer(Encoder, srcBufferWebGPU->Buffer, srcOffset, dstBufferWebGPU->Buffer, dstOffset, size); } void GPUContextWebGPU::UpdateTexture(GPUTexture* texture, int32 arrayIndex, int32 mipIndex, const void* data, uint32 rowPitch, uint32 slicePitch) { ASSERT(texture && texture->IsAllocated() && data); auto textureWebGPU = (GPUTextureWebGPU*)texture; ASSERT_LOW_LAYER(textureWebGPU->Texture && wgpuTextureGetUsage(textureWebGPU->Texture) & WGPUTextureUsage_CopyDst); ASSERT(!texture->IsVolume()); // TODO: impl uploading volume textures (handle write size properly) int32 mipWidth, mipHeight, mipDepth; texture->GetMipSize(mipIndex, mipWidth, mipHeight, mipDepth); WGPUTexelCopyTextureInfo copyInfo = WGPU_TEXEL_COPY_TEXTURE_INFO_INIT; copyInfo.texture = textureWebGPU->Texture; copyInfo.mipLevel = mipIndex; copyInfo.aspect = WGPUTextureAspect_All; WGPUTexelCopyBufferLayout dataLayout = WGPU_TEXEL_COPY_BUFFER_LAYOUT_INIT; dataLayout.bytesPerRow = rowPitch; dataLayout.rowsPerImage = mipHeight; WGPUExtent3D writeSize = { (uint32_t)mipWidth, (uint32_t)mipHeight, 1 }; wgpuQueueWriteTexture(_device->Queue, ©Info, data, slicePitch, &dataLayout, &writeSize); } void GPUContextWebGPU::CopyTexture(GPUTexture* dstResource, uint32 dstSubresource, uint32 dstX, uint32 dstY, uint32 dstZ, GPUTexture* srcResource, uint32 srcSubresource) { ASSERT(dstResource && srcResource); auto srcTextureWebGPU = (GPUTextureWebGPU*)srcResource; auto dstTextureWebGPU = (GPUTextureWebGPU*)dstResource; ASSERT_LOW_LAYER(dstTextureWebGPU->Texture && wgpuTextureGetUsage(dstTextureWebGPU->Texture) & WGPUTextureUsage_CopyDst); ASSERT_LOW_LAYER(srcTextureWebGPU->Texture && wgpuTextureGetUsage(srcTextureWebGPU->Texture) & WGPUTextureUsage_CopySrc); // TODO: handle array/depth slices const int32 srcMipIndex = srcSubresource % srcTextureWebGPU->MipLevels(); const int32 dstMipIndex = dstSubresource % srcTextureWebGPU->MipLevels(); int32 srcMipWidth, srcMipHeight, srcMipDepth; srcTextureWebGPU->GetMipSize(srcMipIndex, srcMipWidth, srcMipHeight, srcMipDepth); WGPUTexelCopyTextureInfo srcInfo = WGPU_TEXEL_COPY_TEXTURE_INFO_INIT; srcInfo.texture = srcTextureWebGPU->Texture; srcInfo.mipLevel = srcMipIndex; srcInfo.aspect = WGPUTextureAspect_All; WGPUTexelCopyTextureInfo dstInfo = WGPU_TEXEL_COPY_TEXTURE_INFO_INIT; dstInfo.texture = dstTextureWebGPU->Texture; dstInfo.mipLevel = dstMipIndex; dstInfo.origin = { dstX, dstY, dstZ }; dstInfo.aspect = WGPUTextureAspect_All; WGPUExtent3D copySize = { (uint32_t)srcMipWidth, (uint32_t)srcMipHeight, (uint32_t)srcMipDepth }; wgpuCommandEncoderCopyTextureToTexture(Encoder, &srcInfo, &dstInfo, ©Size); } void GPUContextWebGPU::ResetCounter(GPUBuffer* buffer) { MISSING_CODE("GPUContextWebGPU::ResetCounter"); } void GPUContextWebGPU::CopyCounter(GPUBuffer* dstBuffer, uint32 dstOffset, GPUBuffer* srcBuffer) { MISSING_CODE("GPUContextWebGPU::CopyCounter"); } void GPUContextWebGPU::CopyResource(GPUResource* dstResource, GPUResource* srcResource) { ASSERT(dstResource && srcResource); auto dstTexture = Cast(dstResource); auto srcTexture = Cast(srcResource); if (srcTexture && dstTexture) { // Texture -> Texture ASSERT(srcTexture->MipLevels() == dstTexture->MipLevels()); ASSERT(srcTexture->ArraySize() == 1); // TODO: implement copying texture arrays for (int32 mipLevel = 0; mipLevel < srcTexture->MipLevels(); mipLevel++) CopyTexture(dstTexture, mipLevel, 0, 0, 0, srcTexture, mipLevel); } else if (srcTexture) { // Texture -> Buffer auto srcTextureWebGPU = (GPUTextureWebGPU*)srcResource; auto dstBufferWebGPU = (GPUBufferWebGPU*)dstResource; MISSING_CODE("GPUContextWebGPU::CopyResource: texture -> buffer"); // TODO: impl this } else if (dstTexture) { // Buffer -> Texture auto srcBufferWebGPU = (GPUBufferWebGPU*)srcResource; auto dstTextureWebGPU = (GPUTextureWebGPU*)dstResource; MISSING_CODE("GPUContextWebGPU::CopyResource: buffer -> texture"); // TODO: impl this } else { // Buffer -> Buffer auto srcBufferWebGPU = (GPUBufferWebGPU*)srcResource; auto dstBufferWebGPU = (GPUBufferWebGPU*)dstResource; uint64 size = Math::Min(srcBufferWebGPU->GetSize(), dstBufferWebGPU->GetSize()); wgpuCommandEncoderCopyBufferToBuffer(Encoder, srcBufferWebGPU->Buffer, 0, dstBufferWebGPU->Buffer, 0, size); } } void GPUContextWebGPU::CopySubresource(GPUResource* dstResource, uint32 dstSubresource, GPUResource* srcResource, uint32 srcSubresource) { ASSERT(dstResource && srcResource); auto dstTexture = Cast(dstResource); auto srcTexture = Cast(srcResource); if (srcTexture && dstTexture) { // Texture -> Texture CopyTexture(dstTexture, dstSubresource, 0, 0, 0, srcTexture, srcSubresource); } else if (srcTexture) { // Texture -> Buffer auto srcTextureWebGPU = (GPUTextureWebGPU*)srcResource; auto dstBufferWebGPU = (GPUBufferWebGPU*)dstResource; MISSING_CODE("GPUContextWebGPU::CopyResource: texture -> buffer"); // TODO: impl this } else if (dstTexture) { // Buffer -> Texture auto srcBufferWebGPU = (GPUBufferWebGPU*)srcResource; auto dstTextureWebGPU = (GPUTextureWebGPU*)dstResource; MISSING_CODE("GPUContextWebGPU::CopyResource: buffer -> texture"); // TODO: impl this } else { // Buffer -> Buffer ASSERT(dstSubresource == 0 && srcSubresource == 0); auto srcBufferWebGPU = (GPUBufferWebGPU*)srcResource; auto dstBufferWebGPU = (GPUBufferWebGPU*)dstResource; uint64 size = Math::Min(srcBufferWebGPU->GetSize(), dstBufferWebGPU->GetSize()); wgpuCommandEncoderCopyBufferToBuffer(Encoder, srcBufferWebGPU->Buffer, 0, dstBufferWebGPU->Buffer, 0, size); } } bool GPUContextWebGPU::FindClear(const GPUTextureViewWebGPU* view, PendingClear& clear) { for (auto& e : _pendingClears) { if (e.View == view) { clear = e; return true; } } return false; } void GPUContextWebGPU::ManualClear(const PendingClear& clear) { // End existing pass (if any) if (_renderPass) { wgpuRenderPassEncoderEnd(_renderPass); wgpuRenderPassEncoderRelease(_renderPass); _renderPass = nullptr; } // Clear with a render pass WGPURenderPassColorAttachment colorAttachment; WGPURenderPassDepthStencilAttachment depthStencilAttachment; WGPURenderPassDescriptor renderPassDesc = WGPU_RENDER_PASS_DESCRIPTOR_INIT; if (((GPUTextureWebGPU*)clear.View->GetParent())->IsDepthStencil()) { renderPassDesc.depthStencilAttachment = &depthStencilAttachment; depthStencilAttachment = WGPU_RENDER_PASS_DEPTH_STENCIL_ATTACHMENT_INIT; depthStencilAttachment.view = clear.View->View; depthStencilAttachment.depthLoadOp = WGPULoadOp_Clear; depthStencilAttachment.depthStoreOp = WGPUStoreOp_Store; depthStencilAttachment.depthClearValue = clear.Depth; depthStencilAttachment.stencilClearValue = clear.Stencil; } else { renderPassDesc.colorAttachmentCount = 1; renderPassDesc.colorAttachments = &colorAttachment; colorAttachment = WGPU_RENDER_PASS_COLOR_ATTACHMENT_INIT; colorAttachment.view = clear.View->View; colorAttachment.depthSlice = clear.View->DepthSlice; colorAttachment.loadOp = WGPULoadOp_Clear; colorAttachment.storeOp = WGPUStoreOp_Store; if (clear.View->HasStencil) { depthStencilAttachment.stencilLoadOp = WGPULoadOp_Clear; depthStencilAttachment.stencilStoreOp = WGPUStoreOp_Store; depthStencilAttachment.stencilClearValue = clear.Stencil; } colorAttachment.clearValue = { clear.RGBA[0], clear.RGBA[1], clear.RGBA[2], clear.RGBA[3] }; } auto renderPass = wgpuCommandEncoderBeginRenderPass(Encoder, &renderPassDesc); wgpuRenderPassEncoderEnd(renderPass); wgpuRenderPassEncoderRelease(renderPass); } void GPUContextWebGPU::OnDrawCall() { // Clear textures that are not bind to the render pass auto renderTargets = ToSpan(_renderTargets, _renderTargetCount); for (int32 i = _pendingClears.Count() - 1; i >= 0; i--) { auto clear = _pendingClears[i]; if (clear.View != _depthStencil && SpanContains(renderTargets, clear.View)) { ManualClear(clear); _pendingClears.RemoveAt(i); } } // Check if need to start a new render pass if (_renderPassDirty) { _renderPassDirty = false; // End existing pass (if any) if (_renderPass) { wgpuRenderPassEncoderEnd(_renderPass); wgpuRenderPassEncoderRelease(_renderPass); } // Start a new render pass WGPURenderPassColorAttachment colorAttachments[GPU_MAX_RT_BINDED]; WGPURenderPassDepthStencilAttachment depthStencilAttachment = WGPU_RENDER_PASS_DEPTH_STENCIL_ATTACHMENT_INIT; WGPURenderPassDescriptor renderPassDesc = WGPU_RENDER_PASS_DESCRIPTOR_INIT; renderPassDesc.colorAttachmentCount = _renderTargetCount; renderPassDesc.colorAttachments = colorAttachments; PendingClear clear; _pipelineKey.MultiSampleCount = 1; _pipelineKey.RenderTargetCount = _renderTargetCount; for (int32 i = 0; i < renderPassDesc.colorAttachmentCount; i++) { auto& colorAttachment = colorAttachments[i]; colorAttachment = WGPU_RENDER_PASS_COLOR_ATTACHMENT_INIT; auto renderTarget = _renderTargets[i]; colorAttachment.view = renderTarget->View; colorAttachment.depthSlice = renderTarget->DepthSlice; colorAttachment.loadOp = WGPULoadOp_Load; colorAttachment.storeOp = WGPUStoreOp_Store; if (FindClear(_depthStencil, clear)) { colorAttachment.loadOp = WGPULoadOp_Clear; colorAttachment.clearValue = { clear.RGBA[0], clear.RGBA[1], clear.RGBA[2], clear.RGBA[3] }; } _pipelineKey.MultiSampleCount = (int32)renderTarget->GetMSAA(); _pipelineKey.RenderTargetFormats[i] = RenderToolsWebGPU::ToTextureFormat(renderTarget->GetFormat()); } if (_depthStencil) { renderPassDesc.depthStencilAttachment = &depthStencilAttachment; depthStencilAttachment.view = _depthStencil->View; depthStencilAttachment.depthLoadOp = WGPULoadOp_Load; depthStencilAttachment.depthStoreOp = _depthStencil->ReadOnly ? WGPUStoreOp_Discard : WGPUStoreOp_Store; depthStencilAttachment.depthReadOnly = _depthStencil->ReadOnly; if (_depthStencil->HasStencil) { depthStencilAttachment.stencilLoadOp = WGPULoadOp_Load; depthStencilAttachment.stencilStoreOp = _depthStencil->ReadOnly ? WGPUStoreOp_Discard : WGPUStoreOp_Store; depthStencilAttachment.depthReadOnly = _depthStencil->ReadOnly; } else { depthStencilAttachment.stencilClearValue = 0; depthStencilAttachment.stencilLoadOp = WGPULoadOp_Clear; depthStencilAttachment.stencilStoreOp = WGPUStoreOp_Discard; depthStencilAttachment.stencilReadOnly = true; } if (FindClear(_depthStencil, clear)) { depthStencilAttachment.depthLoadOp = WGPULoadOp_Clear; depthStencilAttachment.depthClearValue = clear.Depth; if (_depthStencil->HasStencil) { depthStencilAttachment.stencilLoadOp = WGPULoadOp_Clear; depthStencilAttachment.stencilClearValue = clear.Stencil; } } _pipelineKey.DepthStencilFormat = RenderToolsWebGPU::ToTextureFormat(_depthStencil->GetFormat()); } else { _pipelineKey.DepthStencilFormat = WGPUTextureFormat_Undefined; } _renderPass = wgpuCommandEncoderBeginRenderPass(Encoder, &renderPassDesc); ASSERT(_renderPass); // Discard texture clears (done manually or via render pass) _pendingClears.Clear(); // Apply pending state if (_stencilRef != 0) { wgpuRenderPassEncoderSetStencilReference(_renderPass, _stencilRef); } auto scissorRect = _scissorRect; // TODO: skip calling this if scissorRect is default (0, 0, attachment width, attachment height) wgpuRenderPassEncoderSetScissorRect(_renderPass, (uint32_t)scissorRect.GetX(), (uint32_t)scissorRect.GetY(), (uint32_t)scissorRect.GetWidth(), (uint32_t)scissorRect.GetHeight()); auto viewport = _viewport; // TODO: skip calling this if viewport is default (0, 0, attachment width, attachment height, 0, 1) wgpuRenderPassEncoderSetViewport(_renderPass, viewport.X, viewport.Y, viewport.Width, viewport.Height, viewport.MinDepth, viewport.MaxDepth); // Auto-dirty pipeline when new render pass starts if (_pipelineState) _pipelineDirty = true; _indexBufferDirty = true; _vertexBufferDirty = true; _bindGroupDirty = true; if (_blendFactorSet) _blendFactorDirty = true; } // Flush rendering states if (_pipelineDirty) { _pipelineDirty = false; WGPURenderPipeline pipeline = _pipelineState ? _pipelineState->GetPipeline(_pipelineKey) : nullptr; wgpuRenderPassEncoderSetPipeline(_renderPass, pipeline); RENDER_STAT_PS_STATE_CHANGE(); } if (_indexBufferDirty) { _indexBufferDirty = false; wgpuRenderPassEncoderSetIndexBuffer(_renderPass, _indexBuffer.Buffer, _indexBuffer32Bit ? WGPUIndexFormat_Uint32 : WGPUIndexFormat_Uint16, _indexBuffer.Offset, _indexBuffer.Size); } if (_vertexBufferDirty) { _vertexBufferDirty = false; for (int32 i = 0; i < _vertexBufferCount; i++) { auto vb = _vertexBuffers[i]; wgpuRenderPassEncoderSetVertexBuffer(_renderPass, i, vb.Buffer, vb.Offset, vb.Size); } } if (_blendFactorDirty) { _blendFactorDirty = false; WGPUColor color = { _blendFactor.X, _blendFactor.Y, _blendFactor.Z, _blendFactor.W }; wgpuRenderPassEncoderSetBlendConstant(_renderPass, &color); } if (_bindGroupDirty) { _bindGroupDirty = false; // TODO: bind _samplers // TODO: bind _constantBuffers // TODO: bind _shaderResources } } void GPUContextWebGPU::OnDispatch(GPUShaderProgramCS* shader) { // TODO: add compute shaders support } #endif