818 lines
31 KiB
C++
818 lines
31 KiB
C++
// 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<GPUTextureView*>& 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<GPUBuffer*>& 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<GPUConstantBufferWebGPU*>(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<GPUBufferWebGPU*>(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<GPUBufferWebGPU*>(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<GPUTexture>(dstResource);
|
|
auto srcTexture = Cast<GPUTexture>(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<GPUTexture>(dstResource);
|
|
auto srcTexture = Cast<GPUTexture>(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
|