blob: 605c122dd2cc350867e2468d62175f76b526f1e7 [file] [log] [blame]
//
// Copyright 2024 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// ContextWgpu.cpp:
// Implements the class methods for ContextWgpu.
//
#include "libANGLE/renderer/wgpu/ContextWgpu.h"
#include "common/PackedEnums.h"
#include "common/debug.h"
#include "compiler/translator/wgsl/OutputUniformBlocks.h"
#include "libANGLE/Context.h"
#include "libANGLE/renderer/OverlayImpl.h"
#include "libANGLE/renderer/wgpu/BufferWgpu.h"
#include "libANGLE/renderer/wgpu/CompilerWgpu.h"
#include "libANGLE/renderer/wgpu/DisplayWgpu.h"
#include "libANGLE/renderer/wgpu/FenceNVWgpu.h"
#include "libANGLE/renderer/wgpu/FramebufferWgpu.h"
#include "libANGLE/renderer/wgpu/ImageWgpu.h"
#include "libANGLE/renderer/wgpu/ProgramExecutableWgpu.h"
#include "libANGLE/renderer/wgpu/ProgramPipelineWgpu.h"
#include "libANGLE/renderer/wgpu/ProgramWgpu.h"
#include "libANGLE/renderer/wgpu/QueryWgpu.h"
#include "libANGLE/renderer/wgpu/RenderbufferWgpu.h"
#include "libANGLE/renderer/wgpu/SamplerWgpu.h"
#include "libANGLE/renderer/wgpu/ShaderWgpu.h"
#include "libANGLE/renderer/wgpu/SyncWgpu.h"
#include "libANGLE/renderer/wgpu/TextureWgpu.h"
#include "libANGLE/renderer/wgpu/TransformFeedbackWgpu.h"
#include "libANGLE/renderer/wgpu/VertexArrayWgpu.h"
#include "libANGLE/renderer/wgpu/wgpu_pipeline_state.h"
#include "libANGLE/renderer/wgpu/wgpu_utils.h"
namespace rx
{
namespace
{
constexpr angle::PackedEnumMap<webgpu::RenderPassClosureReason, const char *>
kRenderPassClosureReason = {{
{webgpu::RenderPassClosureReason::NewRenderPass,
"Render pass closed due to starting a new render pass"},
{webgpu::RenderPassClosureReason::FramebufferBindingChange,
"Render pass closed due to framebuffer binding change"},
{webgpu::RenderPassClosureReason::FramebufferInternalChange,
"Render pass closed due to framebuffer internal change"},
{webgpu::RenderPassClosureReason::GLFlush, "Render pass closed due to glFlush"},
{webgpu::RenderPassClosureReason::GLFinish, "Render pass closed due to glFinish"},
{webgpu::RenderPassClosureReason::EGLSwapBuffers,
"Render pass closed due to eglSwapBuffers"},
{webgpu::RenderPassClosureReason::GLReadPixels, "Render pass closed due to glReadPixels"},
{webgpu::RenderPassClosureReason::IndexRangeReadback,
"Render pass closed due to index buffer read back for streamed client data"},
{webgpu::RenderPassClosureReason::VertexArrayStreaming,
"Render pass closed for uploading streamed client data"},
{webgpu::RenderPassClosureReason::VertexArrayLineLoop,
"Render pass closed for line loop emulation"},
}};
} // namespace
ContextWgpu::ContextWgpu(const gl::State &state, gl::ErrorSet *errorSet, DisplayWgpu *display)
: ContextImpl(state, errorSet), mDisplay(display)
{
mNewRenderPassDirtyBits = DirtyBits{
DIRTY_BIT_RENDER_PIPELINE_BINDING, // The pipeline needs to be bound for each renderpass
DIRTY_BIT_VIEWPORT,
DIRTY_BIT_SCISSOR,
DIRTY_BIT_BLEND_CONSTANT,
DIRTY_BIT_VERTEX_BUFFERS,
DIRTY_BIT_INDEX_BUFFER,
DIRTY_BIT_BIND_GROUPS,
};
}
ContextWgpu::~ContextWgpu() {}
void ContextWgpu::onDestroy(const gl::Context *context)
{
mImageLoadContext = {};
}
angle::Result ContextWgpu::initialize(const angle::ImageLoadContext &imageLoadContext)
{
mImageLoadContext = imageLoadContext;
return angle::Result::Continue;
}
angle::Result ContextWgpu::onFramebufferChange(FramebufferWgpu *framebufferWgpu,
gl::Command command)
{
// If internal framebuffer state changes, always end the render pass
ANGLE_TRY(endRenderPass(webgpu::RenderPassClosureReason::FramebufferInternalChange));
return angle::Result::Continue;
}
angle::Result ContextWgpu::flush(const gl::Context *context)
{
return flush(webgpu::RenderPassClosureReason::GLFlush);
}
angle::Result ContextWgpu::flush(webgpu::RenderPassClosureReason closureReason)
{
ANGLE_TRY(endRenderPass(closureReason));
if (mCurrentCommandEncoder)
{
wgpu::CommandBuffer commandBuffer = mCurrentCommandEncoder.Finish();
mCurrentCommandEncoder = nullptr;
getQueue().Submit(1, &commandBuffer);
}
return angle::Result::Continue;
}
void ContextWgpu::setColorAttachmentFormat(size_t colorIndex, wgpu::TextureFormat format)
{
if (mRenderPipelineDesc.setColorAttachmentFormat(colorIndex, format))
{
invalidateCurrentRenderPipeline();
}
}
void ContextWgpu::setColorAttachmentFormats(
const gl::DrawBuffersArray<wgpu::TextureFormat> &formats)
{
for (size_t i = 0; i < formats.size(); i++)
{
setColorAttachmentFormat(i, formats[i]);
}
}
void ContextWgpu::setDepthStencilFormat(wgpu::TextureFormat format)
{
if (mRenderPipelineDesc.setDepthStencilAttachmentFormat(format))
{
invalidateCurrentRenderPipeline();
}
}
void ContextWgpu::setVertexAttribute(size_t attribIndex, webgpu::PackedVertexAttribute newAttrib)
{
if (mRenderPipelineDesc.setVertexAttribute(attribIndex, newAttrib))
{
invalidateCurrentRenderPipeline();
}
}
void ContextWgpu::invalidateVertexBuffer(size_t slot)
{
if (mCurrentRenderPipelineAllAttributes[slot])
{
mDirtyBits.set(DIRTY_BIT_VERTEX_BUFFERS);
mDirtyVertexBuffers.set(slot);
}
}
void ContextWgpu::invalidateVertexBuffers()
{
mDirtyBits.set(DIRTY_BIT_VERTEX_BUFFERS);
mDirtyVertexBuffers = mCurrentRenderPipelineAllAttributes;
}
void ContextWgpu::invalidateIndexBuffer()
{
mDirtyBits.set(DIRTY_BIT_INDEX_BUFFER);
}
void ContextWgpu::ensureCommandEncoderCreated()
{
if (!mCurrentCommandEncoder)
{
mCurrentCommandEncoder = getDevice().CreateCommandEncoder(nullptr);
}
}
wgpu::CommandEncoder &ContextWgpu::getCurrentCommandEncoder()
{
return mCurrentCommandEncoder;
}
angle::Result ContextWgpu::finish(const gl::Context *context)
{
ANGLE_TRY(flush(webgpu::RenderPassClosureReason::GLFinish));
wgpu::Future onWorkSubmittedFuture = getQueue().OnSubmittedWorkDone(
wgpu::CallbackMode::WaitAnyOnly, [](wgpu::QueueWorkDoneStatus status) {});
wgpu::WaitStatus status = getInstance().WaitAny(onWorkSubmittedFuture, -1);
ASSERT(!webgpu::IsWgpuError(status));
return angle::Result::Continue;
}
angle::Result ContextWgpu::drawArrays(const gl::Context *context,
gl::PrimitiveMode mode,
GLint first,
GLsizei count)
{
if (mode == gl::PrimitiveMode::TriangleFan)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
uint32_t firstIndex = 0;
uint32_t indexCount = static_cast<uint32_t>(count);
ANGLE_TRY(setupDraw(context, mode, first, count, 1, gl::DrawElementsType::InvalidEnum, nullptr,
0, &firstIndex, &indexCount));
if (mode == gl::PrimitiveMode::LineLoop)
{
mCommandBuffer.drawIndexed(indexCount, 1, firstIndex, 0, 0);
}
else
{
mCommandBuffer.draw(static_cast<uint32_t>(count), 1, static_cast<uint32_t>(first), 0);
}
return angle::Result::Continue;
}
angle::Result ContextWgpu::drawArraysInstanced(const gl::Context *context,
gl::PrimitiveMode mode,
GLint first,
GLsizei count,
GLsizei instanceCount)
{
if (mode == gl::PrimitiveMode::TriangleFan)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
uint32_t firstIndex = 0;
uint32_t indexCount = static_cast<uint32_t>(count);
ANGLE_TRY(setupDraw(context, mode, first, count, instanceCount,
gl::DrawElementsType::InvalidEnum, nullptr, 0, &firstIndex, &indexCount));
if (mode == gl::PrimitiveMode::LineLoop)
{
mCommandBuffer.drawIndexed(indexCount, static_cast<uint32_t>(instanceCount), firstIndex, 0,
0);
}
else
{
mCommandBuffer.draw(indexCount, static_cast<uint32_t>(instanceCount),
static_cast<uint32_t>(first), 0);
}
return angle::Result::Continue;
}
angle::Result ContextWgpu::drawArraysInstancedBaseInstance(const gl::Context *context,
gl::PrimitiveMode mode,
GLint first,
GLsizei count,
GLsizei instanceCount,
GLuint baseInstance)
{
if (mode == gl::PrimitiveMode::TriangleFan)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
uint32_t firstIndex = 0;
uint32_t indexCount = static_cast<uint32_t>(count);
ANGLE_TRY(setupDraw(context, mode, first, count, instanceCount,
gl::DrawElementsType::InvalidEnum, nullptr, 0, &firstIndex, &indexCount));
if (mode == gl::PrimitiveMode::LineLoop)
{
mCommandBuffer.drawIndexed(indexCount, static_cast<uint32_t>(instanceCount), firstIndex, 0,
baseInstance);
}
else
{
mCommandBuffer.draw(static_cast<uint32_t>(count), static_cast<uint32_t>(instanceCount),
static_cast<uint32_t>(first), baseInstance);
}
return angle::Result::Continue;
}
angle::Result ContextWgpu::drawElements(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei count,
gl::DrawElementsType type,
const void *indices)
{
uint32_t firstVertex = 0;
uint32_t indexCount = static_cast<uint32_t>(count);
if (mode == gl::PrimitiveMode::TriangleFan)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
ANGLE_TRY(setupDraw(context, mode, 0, count, 1, type, indices, 0, &firstVertex, &indexCount));
mCommandBuffer.drawIndexed(indexCount, 1, firstVertex, 0, 0);
return angle::Result::Continue;
}
angle::Result ContextWgpu::drawElementsBaseVertex(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei count,
gl::DrawElementsType type,
const void *indices,
GLint baseVertex)
{
uint32_t firstVertex = 0;
uint32_t indexCount = static_cast<uint32_t>(count);
if (mode == gl::PrimitiveMode::TriangleFan)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
ANGLE_TRY(setupDraw(context, mode, 0, count, 1, type, indices, baseVertex, &firstVertex,
&indexCount));
mCommandBuffer.drawIndexed(indexCount, 1, firstVertex, static_cast<uint32_t>(baseVertex), 0);
return angle::Result::Continue;
}
angle::Result ContextWgpu::drawElementsInstanced(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei count,
gl::DrawElementsType type,
const void *indices,
GLsizei instances)
{
uint32_t firstVertex = 0;
uint32_t indexCount = static_cast<uint32_t>(count);
if (mode == gl::PrimitiveMode::TriangleFan)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
ANGLE_TRY(
setupDraw(context, mode, 0, count, instances, type, indices, 0, &firstVertex, &indexCount));
mCommandBuffer.drawIndexed(indexCount, static_cast<uint32_t>(instances), firstVertex, 0, 0);
return angle::Result::Continue;
}
angle::Result ContextWgpu::drawElementsInstancedBaseVertex(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei count,
gl::DrawElementsType type,
const void *indices,
GLsizei instances,
GLint baseVertex)
{
uint32_t firstVertex = 0;
uint32_t indexCount = static_cast<uint32_t>(count);
if (mode == gl::PrimitiveMode::TriangleFan)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
ANGLE_TRY(setupDraw(context, mode, 0, count, instances, type, indices, baseVertex, &firstVertex,
&indexCount));
mCommandBuffer.drawIndexed(indexCount, static_cast<uint32_t>(instances), firstVertex,
static_cast<uint32_t>(baseVertex), 0);
return angle::Result::Continue;
}
angle::Result ContextWgpu::drawElementsInstancedBaseVertexBaseInstance(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei count,
gl::DrawElementsType type,
const void *indices,
GLsizei instances,
GLint baseVertex,
GLuint baseInstance)
{
uint32_t firstVertex = 0;
uint32_t indexCount = static_cast<uint32_t>(count);
if (mode == gl::PrimitiveMode::TriangleFan)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
ANGLE_TRY(setupDraw(context, mode, 0, count, instances, type, indices, baseVertex, &firstVertex,
&indexCount));
mCommandBuffer.drawIndexed(indexCount, static_cast<uint32_t>(instances), firstVertex,
static_cast<uint32_t>(baseVertex),
static_cast<uint32_t>(baseInstance));
return angle::Result::Continue;
}
angle::Result ContextWgpu::drawRangeElements(const gl::Context *context,
gl::PrimitiveMode mode,
GLuint start,
GLuint end,
GLsizei count,
gl::DrawElementsType type,
const void *indices)
{
return drawElements(context, mode, count, type, indices);
}
angle::Result ContextWgpu::drawRangeElementsBaseVertex(const gl::Context *context,
gl::PrimitiveMode mode,
GLuint start,
GLuint end,
GLsizei count,
gl::DrawElementsType type,
const void *indices,
GLint baseVertex)
{
return drawElementsBaseVertex(context, mode, count, type, indices, baseVertex);
}
angle::Result ContextWgpu::drawArraysIndirect(const gl::Context *context,
gl::PrimitiveMode mode,
const void *indirect)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
angle::Result ContextWgpu::drawElementsIndirect(const gl::Context *context,
gl::PrimitiveMode mode,
gl::DrawElementsType type,
const void *indirect)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
angle::Result ContextWgpu::multiDrawArrays(const gl::Context *context,
gl::PrimitiveMode mode,
const GLint *firsts,
const GLsizei *counts,
GLsizei drawcount)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
angle::Result ContextWgpu::multiDrawArraysInstanced(const gl::Context *context,
gl::PrimitiveMode mode,
const GLint *firsts,
const GLsizei *counts,
const GLsizei *instanceCounts,
GLsizei drawcount)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
angle::Result ContextWgpu::multiDrawArraysIndirect(const gl::Context *context,
gl::PrimitiveMode mode,
const void *indirect,
GLsizei drawcount,
GLsizei stride)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
angle::Result ContextWgpu::multiDrawElements(const gl::Context *context,
gl::PrimitiveMode mode,
const GLsizei *counts,
gl::DrawElementsType type,
const GLvoid *const *indices,
GLsizei drawcount)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
angle::Result ContextWgpu::multiDrawElementsInstanced(const gl::Context *context,
gl::PrimitiveMode mode,
const GLsizei *counts,
gl::DrawElementsType type,
const GLvoid *const *indices,
const GLsizei *instanceCounts,
GLsizei drawcount)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
angle::Result ContextWgpu::multiDrawElementsIndirect(const gl::Context *context,
gl::PrimitiveMode mode,
gl::DrawElementsType type,
const void *indirect,
GLsizei drawcount,
GLsizei stride)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
angle::Result ContextWgpu::multiDrawArraysInstancedBaseInstance(const gl::Context *context,
gl::PrimitiveMode mode,
const GLint *firsts,
const GLsizei *counts,
const GLsizei *instanceCounts,
const GLuint *baseInstances,
GLsizei drawcount)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
angle::Result ContextWgpu::multiDrawElementsInstancedBaseVertexBaseInstance(
const gl::Context *context,
gl::PrimitiveMode mode,
const GLsizei *counts,
gl::DrawElementsType type,
const GLvoid *const *indices,
const GLsizei *instanceCounts,
const GLint *baseVertices,
const GLuint *baseInstances,
GLsizei drawcount)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
gl::GraphicsResetStatus ContextWgpu::getResetStatus()
{
return gl::GraphicsResetStatus::NoError;
}
angle::Result ContextWgpu::insertEventMarker(GLsizei length, const char *marker)
{
return angle::Result::Continue;
}
angle::Result ContextWgpu::pushGroupMarker(GLsizei length, const char *marker)
{
return angle::Result::Continue;
}
angle::Result ContextWgpu::popGroupMarker()
{
return angle::Result::Continue;
}
angle::Result ContextWgpu::pushDebugGroup(const gl::Context *context,
GLenum source,
GLuint id,
const std::string &message)
{
return angle::Result::Continue;
}
angle::Result ContextWgpu::popDebugGroup(const gl::Context *context)
{
return angle::Result::Continue;
}
angle::Result ContextWgpu::syncState(const gl::Context *context,
const gl::state::DirtyBits dirtyBits,
const gl::state::DirtyBits bitMask,
const gl::state::ExtendedDirtyBits extendedDirtyBits,
const gl::state::ExtendedDirtyBits extendedBitMask,
gl::Command command)
{
const gl::State &glState = context->getState();
for (auto iter = dirtyBits.begin(), endIter = dirtyBits.end(); iter != endIter; ++iter)
{
size_t dirtyBit = *iter;
switch (dirtyBit)
{
case gl::state::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING:
{
const FramebufferWgpu *framebufferWgpu =
webgpu::GetImpl(context->getState().getDrawFramebuffer());
setColorAttachmentFormats(framebufferWgpu->getCurrentColorAttachmentFormats());
setDepthStencilFormat(framebufferWgpu->getCurrentDepthStencilAttachmentFormat());
ANGLE_TRY(endRenderPass(webgpu::RenderPassClosureReason::FramebufferBindingChange));
}
break;
case gl::state::DIRTY_BIT_READ_FRAMEBUFFER_BINDING:
break;
case gl::state::DIRTY_BIT_SCISSOR_TEST_ENABLED:
mDirtyBits.set(DIRTY_BIT_SCISSOR);
break;
case gl::state::DIRTY_BIT_SCISSOR:
mDirtyBits.set(DIRTY_BIT_SCISSOR);
break;
case gl::state::DIRTY_BIT_VIEWPORT:
mDirtyBits.set(DIRTY_BIT_VIEWPORT);
break;
case gl::state::DIRTY_BIT_DEPTH_RANGE:
mDirtyBits.set(DIRTY_BIT_VIEWPORT);
break;
case gl::state::DIRTY_BIT_BLEND_ENABLED:
{
const gl::BlendStateExt &blendStateExt = mState.getBlendStateExt();
gl::DrawBufferMask enabledMask = blendStateExt.getEnabledMask();
for (size_t i = 0; i < blendStateExt.getDrawBufferCount(); i++)
{
if (mRenderPipelineDesc.setBlendEnabled(i, enabledMask.test(i)))
{
invalidateCurrentRenderPipeline();
}
}
}
break;
case gl::state::DIRTY_BIT_BLEND_COLOR:
mDirtyBits.set(DIRTY_BIT_BLEND_CONSTANT);
break;
case gl::state::DIRTY_BIT_BLEND_FUNCS:
{
const gl::BlendStateExt &blendState = mState.getBlendStateExt();
for (size_t i = 0; i < blendState.getDrawBufferCount(); i++)
{
if (mRenderPipelineDesc.setBlendFuncs(
i, gl_wgpu::GetBlendFactor(blendState.getSrcColorIndexed(i)),
gl_wgpu::GetBlendFactor(blendState.getDstColorIndexed(i)),
gl_wgpu::GetBlendFactor(blendState.getSrcAlphaIndexed(i)),
gl_wgpu::GetBlendFactor(blendState.getDstAlphaIndexed(i))))
{
invalidateCurrentRenderPipeline();
}
}
}
break;
case gl::state::DIRTY_BIT_BLEND_EQUATIONS:
{
const gl::BlendStateExt &blendState = mState.getBlendStateExt();
for (size_t i = 0; i < blendState.getDrawBufferCount(); i++)
{
if (mRenderPipelineDesc.setBlendEquations(
i, gl_wgpu::GetBlendEquation(blendState.getEquationColorIndexed(i)),
gl_wgpu::GetBlendEquation(blendState.getEquationAlphaIndexed(i))))
{
invalidateCurrentRenderPipeline();
}
}
}
break;
case gl::state::DIRTY_BIT_COLOR_MASK:
{
const gl::BlendStateExt &blendStateExt = mState.getBlendStateExt();
for (size_t i = 0; i < blendStateExt.getDrawBufferCount(); i++)
{
bool r, g, b, a;
blendStateExt.getColorMaskIndexed(i, &r, &g, &b, &a);
mRenderPipelineDesc.setColorWriteMask(i, r, g, b, a);
}
invalidateCurrentRenderPipeline();
}
break;
case gl::state::DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED:
break;
case gl::state::DIRTY_BIT_SAMPLE_COVERAGE_ENABLED:
break;
case gl::state::DIRTY_BIT_SAMPLE_COVERAGE:
break;
case gl::state::DIRTY_BIT_SAMPLE_MASK_ENABLED:
break;
case gl::state::DIRTY_BIT_SAMPLE_MASK:
break;
case gl::state::DIRTY_BIT_DEPTH_TEST_ENABLED:
// Enabled and func get combined into one state in WebGPU. Only sync it once.
iter.setLaterBit(gl::state::DIRTY_BIT_DEPTH_FUNC);
break;
case gl::state::DIRTY_BIT_DEPTH_FUNC:
if (mRenderPipelineDesc.setDepthFunc(
gl_wgpu::GetCompareFunc(glState.getDepthStencilState().depthFunc,
glState.getDepthStencilState().depthTest)))
{
invalidateCurrentRenderPipeline();
}
break;
case gl::state::DIRTY_BIT_DEPTH_MASK:
break;
case gl::state::DIRTY_BIT_STENCIL_TEST_ENABLED:
// Changing the state of stencil test affects both the front and back funcs.
iter.setLaterBit(gl::state::DIRTY_BIT_STENCIL_FUNCS_FRONT);
iter.setLaterBit(gl::state::DIRTY_BIT_STENCIL_FUNCS_BACK);
break;
case gl::state::DIRTY_BIT_STENCIL_FUNCS_FRONT:
if (mRenderPipelineDesc.setStencilFrontFunc(
gl_wgpu::GetCompareFunc(glState.getDepthStencilState().stencilFunc,
glState.getDepthStencilState().stencilTest)))
{
invalidateCurrentRenderPipeline();
}
break;
case gl::state::DIRTY_BIT_STENCIL_FUNCS_BACK:
if (mRenderPipelineDesc.setStencilBackFunc(
gl_wgpu::GetCompareFunc(glState.getDepthStencilState().stencilBackFunc,
glState.getDepthStencilState().stencilTest)))
{
invalidateCurrentRenderPipeline();
}
break;
case gl::state::DIRTY_BIT_STENCIL_OPS_FRONT:
{
wgpu::StencilOperation failOp =
gl_wgpu::getStencilOp(glState.getDepthStencilState().stencilFail);
wgpu::StencilOperation depthFailOp =
gl_wgpu::getStencilOp(glState.getDepthStencilState().stencilPassDepthFail);
wgpu::StencilOperation passOp =
gl_wgpu::getStencilOp(glState.getDepthStencilState().stencilPassDepthPass);
if (mRenderPipelineDesc.setStencilFrontOps(failOp, depthFailOp, passOp))
{
invalidateCurrentRenderPipeline();
}
}
break;
case gl::state::DIRTY_BIT_STENCIL_OPS_BACK:
{
wgpu::StencilOperation failOp =
gl_wgpu::getStencilOp(glState.getDepthStencilState().stencilBackFail);
wgpu::StencilOperation depthFailOp =
gl_wgpu::getStencilOp(glState.getDepthStencilState().stencilBackPassDepthFail);
wgpu::StencilOperation passOp =
gl_wgpu::getStencilOp(glState.getDepthStencilState().stencilBackPassDepthPass);
if (mRenderPipelineDesc.setStencilBackOps(failOp, depthFailOp, passOp))
{
invalidateCurrentRenderPipeline();
}
}
break;
case gl::state::DIRTY_BIT_STENCIL_WRITEMASK_FRONT:
if (mRenderPipelineDesc.setStencilWriteMask(
glState.getDepthStencilState().stencilWritemask))
{
invalidateCurrentRenderPipeline();
}
break;
case gl::state::DIRTY_BIT_STENCIL_WRITEMASK_BACK:
break;
case gl::state::DIRTY_BIT_CULL_FACE_ENABLED:
case gl::state::DIRTY_BIT_CULL_FACE:
mRenderPipelineDesc.setCullMode(glState.getRasterizerState().cullMode,
glState.getRasterizerState().cullFace);
invalidateCurrentRenderPipeline();
break;
case gl::state::DIRTY_BIT_FRONT_FACE:
mRenderPipelineDesc.setFrontFace(glState.getRasterizerState().frontFace);
invalidateCurrentRenderPipeline();
break;
case gl::state::DIRTY_BIT_POLYGON_OFFSET_FILL_ENABLED:
break;
case gl::state::DIRTY_BIT_POLYGON_OFFSET:
break;
case gl::state::DIRTY_BIT_RASTERIZER_DISCARD_ENABLED:
break;
case gl::state::DIRTY_BIT_LINE_WIDTH:
break;
case gl::state::DIRTY_BIT_PRIMITIVE_RESTART_ENABLED:
break;
case gl::state::DIRTY_BIT_CLEAR_COLOR:
break;
case gl::state::DIRTY_BIT_CLEAR_DEPTH:
break;
case gl::state::DIRTY_BIT_CLEAR_STENCIL:
break;
case gl::state::DIRTY_BIT_UNPACK_STATE:
break;
case gl::state::DIRTY_BIT_UNPACK_BUFFER_BINDING:
break;
case gl::state::DIRTY_BIT_PACK_STATE:
break;
case gl::state::DIRTY_BIT_PACK_BUFFER_BINDING:
break;
case gl::state::DIRTY_BIT_DITHER_ENABLED:
break;
case gl::state::DIRTY_BIT_RENDERBUFFER_BINDING:
break;
case gl::state::DIRTY_BIT_VERTEX_ARRAY_BINDING:
invalidateCurrentRenderPipeline();
break;
case gl::state::DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING:
break;
case gl::state::DIRTY_BIT_DISPATCH_INDIRECT_BUFFER_BINDING:
break;
case gl::state::DIRTY_BIT_PROGRAM_BINDING:
case gl::state::DIRTY_BIT_PROGRAM_EXECUTABLE:
invalidateCurrentRenderPipeline();
break;
case gl::state::DIRTY_BIT_SAMPLER_BINDINGS:
break;
case gl::state::DIRTY_BIT_TEXTURE_BINDINGS:
break;
case gl::state::DIRTY_BIT_IMAGE_BINDINGS:
break;
case gl::state::DIRTY_BIT_TRANSFORM_FEEDBACK_BINDING:
break;
case gl::state::DIRTY_BIT_UNIFORM_BUFFER_BINDINGS:
break;
case gl::state::DIRTY_BIT_SHADER_STORAGE_BUFFER_BINDING:
break;
case gl::state::DIRTY_BIT_ATOMIC_COUNTER_BUFFER_BINDING:
break;
case gl::state::DIRTY_BIT_MULTISAMPLING:
break;
case gl::state::DIRTY_BIT_SAMPLE_ALPHA_TO_ONE:
break;
case gl::state::DIRTY_BIT_COVERAGE_MODULATION:
break;
case gl::state::DIRTY_BIT_FRAMEBUFFER_SRGB_WRITE_CONTROL_MODE:
break;
case gl::state::DIRTY_BIT_CURRENT_VALUES:
break;
case gl::state::DIRTY_BIT_PROVOKING_VERTEX:
break;
case gl::state::DIRTY_BIT_SAMPLE_SHADING:
break;
case gl::state::DIRTY_BIT_PATCH_VERTICES:
break;
case gl::state::DIRTY_BIT_EXTENDED:
{
for (auto extendedIter = extendedDirtyBits.begin(),
extendedEndIter = extendedDirtyBits.end();
extendedIter != extendedEndIter; ++extendedIter)
{
const size_t extendedDirtyBit = *extendedIter;
switch (extendedDirtyBit)
{
case gl::state::EXTENDED_DIRTY_BIT_CLIP_CONTROL:
break;
case gl::state::EXTENDED_DIRTY_BIT_CLIP_DISTANCES:
break;
case gl::state::EXTENDED_DIRTY_BIT_DEPTH_CLAMP_ENABLED:
break;
case gl::state::EXTENDED_DIRTY_BIT_MIPMAP_GENERATION_HINT:
break;
case gl::state::EXTENDED_DIRTY_BIT_POLYGON_MODE:
break;
case gl::state::EXTENDED_DIRTY_BIT_POLYGON_OFFSET_POINT_ENABLED:
break;
case gl::state::EXTENDED_DIRTY_BIT_POLYGON_OFFSET_LINE_ENABLED:
break;
case gl::state::EXTENDED_DIRTY_BIT_SHADER_DERIVATIVE_HINT:
break;
case gl::state::EXTENDED_DIRTY_BIT_SHADING_RATE:
break;
case gl::state::EXTENDED_DIRTY_BIT_LOGIC_OP_ENABLED:
break;
case gl::state::EXTENDED_DIRTY_BIT_LOGIC_OP:
break;
case gl::state::EXTENDED_DIRTY_BIT_BLEND_ADVANCED_COHERENT:
break;
default:
UNREACHABLE();
}
}
}
break;
default:
UNREACHABLE();
break;
}
}
return angle::Result::Continue;
}
GLint ContextWgpu::getGPUDisjoint()
{
return 0;
}
GLint64 ContextWgpu::getTimestamp()
{
return 0;
}
angle::Result ContextWgpu::onMakeCurrent(const gl::Context *context)
{
return angle::Result::Continue;
}
gl::Caps ContextWgpu::getNativeCaps() const
{
return mDisplay->getGLCaps();
}
const gl::TextureCapsMap &ContextWgpu::getNativeTextureCaps() const
{
return mDisplay->getGLTextureCaps();
}
const gl::Extensions &ContextWgpu::getNativeExtensions() const
{
return mDisplay->getGLExtensions();
}
const gl::Limitations &ContextWgpu::getNativeLimitations() const
{
return mDisplay->getGLLimitations();
}
const ShPixelLocalStorageOptions &ContextWgpu::getNativePixelLocalStorageOptions() const
{
return mDisplay->getPLSOptions();
}
CompilerImpl *ContextWgpu::createCompiler()
{
return new CompilerWgpu();
}
ShaderImpl *ContextWgpu::createShader(const gl::ShaderState &data)
{
return new ShaderWgpu(data);
}
ProgramImpl *ContextWgpu::createProgram(const gl::ProgramState &data)
{
return new ProgramWgpu(data);
}
ProgramExecutableImpl *ContextWgpu::createProgramExecutable(const gl::ProgramExecutable *executable)
{
return new ProgramExecutableWgpu(executable);
}
FramebufferImpl *ContextWgpu::createFramebuffer(const gl::FramebufferState &data)
{
return new FramebufferWgpu(data);
}
TextureImpl *ContextWgpu::createTexture(const gl::TextureState &state)
{
return new TextureWgpu(state);
}
RenderbufferImpl *ContextWgpu::createRenderbuffer(const gl::RenderbufferState &state)
{
return new RenderbufferWgpu(state);
}
BufferImpl *ContextWgpu::createBuffer(const gl::BufferState &state)
{
return new BufferWgpu(state);
}
VertexArrayImpl *ContextWgpu::createVertexArray(const gl::VertexArrayState &data)
{
return new VertexArrayWgpu(data);
}
QueryImpl *ContextWgpu::createQuery(gl::QueryType type)
{
return new QueryWgpu(type);
}
FenceNVImpl *ContextWgpu::createFenceNV()
{
return new FenceNVWgpu();
}
SyncImpl *ContextWgpu::createSync()
{
return new SyncWgpu();
}
TransformFeedbackImpl *ContextWgpu::createTransformFeedback(const gl::TransformFeedbackState &state)
{
return new TransformFeedbackWgpu(state);
}
SamplerImpl *ContextWgpu::createSampler(const gl::SamplerState &state)
{
return new SamplerWgpu(state);
}
ProgramPipelineImpl *ContextWgpu::createProgramPipeline(const gl::ProgramPipelineState &state)
{
return new ProgramPipelineWgpu(state);
}
MemoryObjectImpl *ContextWgpu::createMemoryObject()
{
UNREACHABLE();
return nullptr;
}
SemaphoreImpl *ContextWgpu::createSemaphore()
{
UNREACHABLE();
return nullptr;
}
OverlayImpl *ContextWgpu::createOverlay(const gl::OverlayState &state)
{
return new OverlayImpl(state);
}
angle::Result ContextWgpu::dispatchCompute(const gl::Context *context,
GLuint numGroupsX,
GLuint numGroupsY,
GLuint numGroupsZ)
{
return angle::Result::Continue;
}
angle::Result ContextWgpu::dispatchComputeIndirect(const gl::Context *context, GLintptr indirect)
{
return angle::Result::Continue;
}
angle::Result ContextWgpu::memoryBarrier(const gl::Context *context, GLbitfield barriers)
{
return angle::Result::Continue;
}
angle::Result ContextWgpu::memoryBarrierByRegion(const gl::Context *context, GLbitfield barriers)
{
return angle::Result::Continue;
}
void ContextWgpu::handleError(GLenum errorCode,
const char *message,
const char *file,
const char *function,
unsigned int line)
{
std::stringstream errorStream;
errorStream << "Internal Wgpu back-end error: " << message << ".";
mErrors->handleError(errorCode, errorStream.str().c_str(), file, function, line);
}
angle::Result ContextWgpu::startRenderPass(const wgpu::RenderPassDescriptor &desc)
{
ensureCommandEncoderCreated();
mCurrentRenderPass = mCurrentCommandEncoder.BeginRenderPass(&desc);
mDirtyBits |= mNewRenderPassDirtyBits;
return angle::Result::Continue;
}
angle::Result ContextWgpu::endRenderPass(webgpu::RenderPassClosureReason closureReason)
{
if (mCurrentRenderPass)
{
const char *reasonText = kRenderPassClosureReason[closureReason];
ASSERT(reasonText);
if (mCommandBuffer.hasCommands())
{
ANGLE_WGPU_SCOPED_DEBUG_TRY(this, mCommandBuffer.recordCommands(mCurrentRenderPass));
mCommandBuffer.clear();
}
mCurrentRenderPass.End();
mCurrentRenderPass = nullptr;
}
mDirtyBits.set(DIRTY_BIT_RENDER_PASS);
return angle::Result::Continue;
}
angle::Result ContextWgpu::setupDraw(const gl::Context *context,
gl::PrimitiveMode mode,
GLint firstVertexOrInvalid,
GLsizei vertexOrIndexCount,
GLsizei instanceCount,
gl::DrawElementsType indexTypeOrInvalid,
const void *indices,
GLint baseVertex,
uint32_t *outFirstIndex,
uint32_t *indexCountOut)
{
gl::DrawElementsType dstDndexTypeOrInvalid = indexTypeOrInvalid;
if (mode == gl::PrimitiveMode::LineLoop &&
dstDndexTypeOrInvalid == gl::DrawElementsType::InvalidEnum)
{
if (vertexOrIndexCount >= std::numeric_limits<unsigned short>::max())
{
dstDndexTypeOrInvalid = gl::DrawElementsType::UnsignedInt;
}
else
{
dstDndexTypeOrInvalid = gl::DrawElementsType::UnsignedShort;
}
}
if (mRenderPipelineDesc.setPrimitiveMode(mode, dstDndexTypeOrInvalid))
{
invalidateCurrentRenderPipeline();
}
ProgramExecutableWgpu *executableWgpu = webgpu::GetImpl(mState.getProgramExecutable());
if (executableWgpu->checkDirtyUniforms())
{
mDirtyBits.set(DIRTY_BIT_BIND_GROUPS);
}
const void *adjustedIndicesPtr = indices;
if (mState.areClientArraysEnabled())
{
VertexArrayWgpu *vertexArrayWgpu = GetImplAs<VertexArrayWgpu>(mState.getVertexArray());
// Pass in original indexTypeOrInvalid into syncClientArrays because the method will need to
// determine if the original draw call was a DrawElements or DrawArrays call.
ANGLE_TRY(vertexArrayWgpu->syncClientArrays(
context, mState.getProgramExecutable()->getActiveAttribLocationsMask(), mode,
firstVertexOrInvalid, vertexOrIndexCount, instanceCount, indexTypeOrInvalid, indices,
baseVertex, mState.isPrimitiveRestartEnabled(), &adjustedIndicesPtr, indexCountOut));
}
bool reAddDirtyIndexBufferBit = false;
if (dstDndexTypeOrInvalid != gl::DrawElementsType::InvalidEnum)
{
*outFirstIndex =
gl_wgpu::GetFirstIndexForDrawCall(dstDndexTypeOrInvalid, adjustedIndicesPtr);
if (mCurrentIndexBufferType != dstDndexTypeOrInvalid)
{
invalidateIndexBuffer();
}
}
if (mDirtyBits.any())
{
for (DirtyBits::Iterator dirtyBitIter = mDirtyBits.begin();
dirtyBitIter != mDirtyBits.end(); ++dirtyBitIter)
{
size_t dirtyBit = *dirtyBitIter;
switch (dirtyBit)
{
case DIRTY_BIT_RENDER_PIPELINE_DESC:
ANGLE_TRY(handleDirtyRenderPipelineDesc(&dirtyBitIter));
break;
case DIRTY_BIT_RENDER_PASS:
ANGLE_TRY(handleDirtyRenderPass(&dirtyBitIter));
break;
case DIRTY_BIT_RENDER_PIPELINE_BINDING:
ANGLE_TRY(handleDirtyRenderPipelineBinding(&dirtyBitIter));
break;
case DIRTY_BIT_VIEWPORT:
ANGLE_TRY(handleDirtyViewport(&dirtyBitIter));
break;
case DIRTY_BIT_SCISSOR:
ANGLE_TRY(handleDirtyScissor(&dirtyBitIter));
break;
case DIRTY_BIT_BLEND_CONSTANT:
ANGLE_TRY(handleDirtyBlendConstant(&dirtyBitIter));
break;
case DIRTY_BIT_VERTEX_BUFFERS:
ANGLE_TRY(handleDirtyVertexBuffers(mDirtyVertexBuffers, &dirtyBitIter));
mDirtyVertexBuffers.reset();
break;
case DIRTY_BIT_INDEX_BUFFER:
if (dstDndexTypeOrInvalid != gl::DrawElementsType::InvalidEnum)
{
ANGLE_TRY(handleDirtyIndexBuffer(dstDndexTypeOrInvalid, &dirtyBitIter));
}
else
{
// If this is not an indexed draw call, don't sync the index buffer. Save it
// for a future indexed draw call when we know what index type to use
reAddDirtyIndexBufferBit = true;
}
break;
case DIRTY_BIT_BIND_GROUPS:
ANGLE_TRY(handleDirtyBindGroups(&dirtyBitIter));
break;
default:
UNREACHABLE();
break;
}
}
if (reAddDirtyIndexBufferBit)
{
// Re-add the index buffer dirty bit for a future indexed draw call.
mDirtyBits.reset(DIRTY_BIT_INDEX_BUFFER);
}
mDirtyBits.reset();
}
return angle::Result::Continue;
}
angle::Result ContextWgpu::handleDirtyRenderPipelineDesc(DirtyBits::Iterator *dirtyBitsIterator)
{
ASSERT(mState.getProgramExecutable() != nullptr);
ProgramExecutableWgpu *executable = webgpu::GetImpl(mState.getProgramExecutable());
ASSERT(executable);
wgpu::RenderPipeline previousPipeline = std::move(mCurrentGraphicsPipeline);
ANGLE_TRY(executable->getRenderPipeline(this, mRenderPipelineDesc, &mCurrentGraphicsPipeline));
if (mCurrentGraphicsPipeline != previousPipeline)
{
dirtyBitsIterator->setLaterBit(DIRTY_BIT_RENDER_PIPELINE_BINDING);
}
mCurrentRenderPipelineAllAttributes =
executable->getExecutable()->getActiveAttribLocationsMask();
return angle::Result::Continue;
}
angle::Result ContextWgpu::handleDirtyRenderPipelineBinding(DirtyBits::Iterator *dirtyBitsIterator)
{
ASSERT(mCurrentGraphicsPipeline);
mCommandBuffer.setPipeline(mCurrentGraphicsPipeline);
return angle::Result::Continue;
}
angle::Result ContextWgpu::handleDirtyViewport(DirtyBits::Iterator *dirtyBitsIterator)
{
const gl::Framebuffer *framebuffer = mState.getDrawFramebuffer();
const gl::Extents &framebufferSize = framebuffer->getExtents();
const gl::Rectangle framebufferRect(0, 0, framebufferSize.width, framebufferSize.height);
gl::Rectangle clampedViewport;
if (!ClipRectangle(mState.getViewport(), framebufferRect, &clampedViewport))
{
clampedViewport = gl::Rectangle(0, 0, 1, 1);
}
float depthMin = mState.getNearPlane();
float depthMax = mState.getFarPlane();
// This clamping should be done by the front end. WebGPU requires values in this range.
ASSERT(depthMin >= 0 && depthMin <= 1);
ASSERT(depthMin >= 0 && depthMin <= 1);
// WebGPU requires that the maxDepth is at least minDepth. WebGL requires the same but core GL
// ES does not.
if (depthMin > depthMax)
{
UNIMPLEMENTED();
}
bool isDefaultViewport = (clampedViewport == framebufferRect) && depthMin == 0 && depthMax == 1;
if (isDefaultViewport && !mCommandBuffer.hasSetViewportCommand())
{
// Each render pass has a default viewport set equal to the size of the render targets. We
// can skip setting the viewport.
return angle::Result::Continue;
}
ASSERT(mCurrentGraphicsPipeline);
mCommandBuffer.setViewport(clampedViewport.x, clampedViewport.y, clampedViewport.width,
clampedViewport.height, depthMin, depthMax);
return angle::Result::Continue;
}
angle::Result ContextWgpu::handleDirtyScissor(DirtyBits::Iterator *dirtyBitsIterator)
{
const gl::Framebuffer *framebuffer = mState.getDrawFramebuffer();
const gl::Extents &framebufferSize = framebuffer->getExtents();
const gl::Rectangle framebufferRect(0, 0, framebufferSize.width, framebufferSize.height);
gl::Rectangle clampedScissor = framebufferRect;
// When the GL scissor test is disabled, set the scissor to the entire size of the framebuffer
if (mState.isScissorTestEnabled())
{
if (!ClipRectangle(mState.getScissor(), framebufferRect, &clampedScissor))
{
clampedScissor = gl::Rectangle(0, 0, 0, 0);
}
}
bool isDefaultScissor = clampedScissor == framebufferRect;
if (isDefaultScissor && !mCommandBuffer.hasSetScissorCommand())
{
// Each render pass has a default scissor set equal to the size of the render targets. We
// can skip setting the scissor.
return angle::Result::Continue;
}
ASSERT(mCurrentGraphicsPipeline);
mCommandBuffer.setScissorRect(clampedScissor.x, clampedScissor.y, clampedScissor.width,
clampedScissor.height);
return angle::Result::Continue;
}
angle::Result ContextWgpu::handleDirtyBlendConstant(DirtyBits::Iterator *dirtyBitsIterator)
{
const gl::ColorF &blendColor = mState.getBlendColor();
bool isDefaultBlendConstant = blendColor.red == 0 && blendColor.green == 0 &&
blendColor.blue == 0 && blendColor.alpha == 0;
if (isDefaultBlendConstant && !mCommandBuffer.hasSetBlendConstantCommand())
{
// Each render pass has a default blend constant set to all zeroes. We can skip setting it.
return angle::Result::Continue;
}
ASSERT(mCurrentGraphicsPipeline);
mCommandBuffer.setBlendConstant(blendColor.red, blendColor.green, blendColor.blue,
blendColor.alpha);
return angle::Result::Continue;
}
angle::Result ContextWgpu::handleDirtyRenderPass(DirtyBits::Iterator *dirtyBitsIterator)
{
FramebufferWgpu *drawFramebufferWgpu = webgpu::GetImpl(mState.getDrawFramebuffer());
ANGLE_TRY(drawFramebufferWgpu->startNewRenderPass(this));
dirtyBitsIterator->setLaterBits(mNewRenderPassDirtyBits);
mDirtyVertexBuffers = mCurrentRenderPipelineAllAttributes;
return angle::Result::Continue;
}
angle::Result ContextWgpu::handleDirtyVertexBuffers(const gl::AttributesMask &slots,
DirtyBits::Iterator *dirtyBitsIterator)
{
VertexArrayWgpu *vertexArrayWgpu = GetImplAs<VertexArrayWgpu>(mState.getVertexArray());
for (size_t slot : slots)
{
const VertexBufferWithOffset &buffer = vertexArrayWgpu->getVertexBuffer(slot);
if (!buffer.buffer)
{
// Missing default attribute support
ASSERT(!mState.getVertexArray()->getVertexAttribute(slot).enabled);
UNIMPLEMENTED();
continue;
}
if (buffer.buffer->getMappedState())
{
ANGLE_TRY(buffer.buffer->unmap());
}
mCommandBuffer.setVertexBuffer(static_cast<uint32_t>(slot), buffer.buffer->getBuffer(),
buffer.offset, WGPU_WHOLE_SIZE);
}
return angle::Result::Continue;
}
angle::Result ContextWgpu::handleDirtyIndexBuffer(gl::DrawElementsType indexType,
DirtyBits::Iterator *dirtyBitsIterator)
{
VertexArrayWgpu *vertexArrayWgpu = GetImplAs<VertexArrayWgpu>(mState.getVertexArray());
webgpu::BufferHelper *buffer = vertexArrayWgpu->getIndexBuffer();
ASSERT(buffer);
if (buffer->getMappedState())
{
ANGLE_TRY(buffer->unmap());
}
mCommandBuffer.setIndexBuffer(buffer->getBuffer(), gl_wgpu::GetIndexFormat(indexType), 0, -1);
mCurrentIndexBufferType = indexType;
return angle::Result::Continue;
}
angle::Result ContextWgpu::handleDirtyBindGroups(DirtyBits::Iterator *dirtyBitsIterator)
{
ProgramExecutableWgpu *executableWgpu = webgpu::GetImpl(mState.getProgramExecutable());
wgpu::BindGroup bindGroup;
ANGLE_TRY(executableWgpu->updateUniformsAndGetBindGroup(this, &bindGroup));
// TODO(anglebug.com/376553328): need to set up every bind group here.
mCommandBuffer.setBindGroup(sh::kDefaultUniformBlockBindGroup, bindGroup);
return angle::Result::Continue;
}
} // namespace rx