/* * Copyright 2019 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/gpu/dawn/GrDawnGpuCommandBuffer.h" #include "src/gpu/GrFixedClip.h" #include "src/gpu/GrMesh.h" #include "src/gpu/GrOpFlushState.h" #include "src/gpu/GrPipeline.h" #include "src/gpu/GrRenderTargetPriv.h" #include "src/gpu/GrTexturePriv.h" #include "src/gpu/dawn/GrDawnBuffer.h" #include "src/gpu/dawn/GrDawnGpu.h" #include "src/gpu/dawn/GrDawnProgramBuilder.h" #include "src/gpu/dawn/GrDawnRenderTarget.h" #include "src/gpu/dawn/GrDawnStencilAttachment.h" #include "src/gpu/dawn/GrDawnTexture.h" #include "src/gpu/dawn/GrDawnUtil.h" #include "src/sksl/SkSLCompiler.h" GrDawnGpuTextureCommandBuffer::GrDawnGpuTextureCommandBuffer(GrDawnGpu* gpu, GrTexture* texture, GrSurfaceOrigin origin) : INHERITED(texture, origin) , fGpu(gpu) { fEncoder = fGpu->device().CreateCommandEncoder(); } void GrDawnGpuTextureCommandBuffer::copy(GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) { if (!src->asTexture()) { return; } uint32_t width = srcRect.width(), height = srcRect.height(); size_t rowBytes = srcRect.width() * GrBytesPerPixel(src->config()); rowBytes = GrDawnRoundRowBytes(rowBytes); size_t sizeInBytes = height * rowBytes; dawn::BufferDescriptor desc; desc.usage = dawn::BufferUsageBit::CopySrc | dawn::BufferUsageBit::CopyDst; desc.size = sizeInBytes; dawn::Buffer buffer = fGpu->device().CreateBuffer(&desc); dawn::TextureCopyView srcTextureView, dstTextureView; srcTextureView.texture = static_cast(src->asTexture())->texture(); srcTextureView.origin = {(uint32_t) srcRect.x(), (uint32_t) srcRect.y(), 0}; dstTextureView.texture = static_cast(fTexture)->texture(); dstTextureView.origin = {(uint32_t) dstPoint.x(), (uint32_t) dstPoint.y(), 0}; dawn::BufferCopyView bufferView; bufferView.buffer = buffer; bufferView.offset = 0; bufferView.rowPitch = rowBytes; bufferView.imageHeight = height; dawn::Extent3D copySize = {width, height, 1}; fEncoder.CopyTextureToBuffer(&srcTextureView, &bufferView, ©Size); fEncoder.CopyBufferToTexture(&bufferView, &dstTextureView, ©Size); } void GrDawnGpuTextureCommandBuffer::transferFrom(const SkIRect& srcRect, GrColorType surfaceColorType, GrColorType bufferColorType, GrGpuBuffer* transferBuffer, size_t offset) { fGpu->transferPixelsFrom(fTexture, srcRect.fLeft, srcRect.fTop, srcRect.width(), srcRect.height(), surfaceColorType, bufferColorType, transferBuffer, offset); } void GrDawnGpuTextureCommandBuffer::submit() { dawn::CommandBuffer commandBuffer = fEncoder.Finish(); if (commandBuffer) { fGpu->queue().Submit(1, &commandBuffer); } } GrDawnGpuTextureCommandBuffer::~GrDawnGpuTextureCommandBuffer() {} //////////////////////////////////////////////////////////////////////////////// static dawn::LoadOp to_dawn_load_op(GrLoadOp loadOp) { switch (loadOp) { case GrLoadOp::kLoad: return dawn::LoadOp::Load; case GrLoadOp::kDiscard: // Use LoadOp::Load to emulate DontCare. // Dawn doesn't have DontCare, for security reasons. // Load should be equivalent to DontCare for desktop; Clear would // probably be better for tilers. If Dawn does add DontCare // as an extension, use it here. return dawn::LoadOp::Load; case GrLoadOp::kClear: return dawn::LoadOp::Clear; default: SK_ABORT("Invalid LoadOp"); } } GrDawnGpuRTCommandBuffer::GrDawnGpuRTCommandBuffer(GrDawnGpu* gpu, GrRenderTarget* rt, GrSurfaceOrigin origin, const LoadAndStoreInfo& colorInfo, const StencilLoadAndStoreInfo& stencilInfo) : INHERITED(rt, origin) , fGpu(gpu) , fColorInfo(colorInfo) { fEncoder = fGpu->device().CreateCommandEncoder(); dawn::LoadOp colorOp = to_dawn_load_op(colorInfo.fLoadOp); dawn::LoadOp stencilOp = to_dawn_load_op(stencilInfo.fLoadOp); fPassEncoder = beginRenderPass(colorOp, stencilOp); } dawn::RenderPassEncoder GrDawnGpuRTCommandBuffer::beginRenderPass(dawn::LoadOp colorOp, dawn::LoadOp stencilOp) { dawn::Texture texture = static_cast(fRenderTarget)->texture(); auto stencilAttachment = static_cast( fRenderTarget->renderTargetPriv().getStencilAttachment()); dawn::TextureView colorView = texture.CreateDefaultView(); const float *c = fColorInfo.fClearColor.vec(); dawn::RenderPassColorAttachmentDescriptor colorAttachment; colorAttachment.attachment = colorView; colorAttachment.resolveTarget = nullptr; colorAttachment.clearColor = { c[0], c[1], c[2], c[3] }; colorAttachment.loadOp = colorOp; colorAttachment.storeOp = dawn::StoreOp::Store; dawn::RenderPassColorAttachmentDescriptor* colorAttachments = { &colorAttachment }; dawn::RenderPassDescriptor renderPassDescriptor; renderPassDescriptor.colorAttachmentCount = 1; renderPassDescriptor.colorAttachments = &colorAttachments; if (stencilAttachment) { dawn::RenderPassDepthStencilAttachmentDescriptor depthStencilAttachment; depthStencilAttachment.attachment = stencilAttachment->view(); depthStencilAttachment.depthLoadOp = stencilOp; depthStencilAttachment.stencilLoadOp = stencilOp; depthStencilAttachment.clearDepth = 1.0f; depthStencilAttachment.clearStencil = 0; depthStencilAttachment.depthStoreOp = dawn::StoreOp::Store; depthStencilAttachment.stencilStoreOp = dawn::StoreOp::Store; renderPassDescriptor.depthStencilAttachment = &depthStencilAttachment; } else { renderPassDescriptor.depthStencilAttachment = nullptr; } return fEncoder.BeginRenderPass(&renderPassDescriptor); } GrDawnGpuRTCommandBuffer::~GrDawnGpuRTCommandBuffer() { } GrGpu* GrDawnGpuRTCommandBuffer::gpu() { return fGpu; } void GrDawnGpuRTCommandBuffer::end() { fPassEncoder.EndPass(); } void GrDawnGpuRTCommandBuffer::submit() { dawn::CommandBuffer commandBuffer = fEncoder.Finish(); if (commandBuffer) { fGpu->queue().Submit(1, &commandBuffer); } } void GrDawnGpuRTCommandBuffer::insertEventMarker(const char* msg) { SkASSERT(!"unimplemented"); } void GrDawnGpuRTCommandBuffer::transferFrom(const SkIRect& srcRect, GrColorType surfaceColorType, GrColorType bufferColorType, GrGpuBuffer* transferBuffer, size_t offset) { fGpu->transferPixelsFrom(fRenderTarget, srcRect.fLeft, srcRect.fTop, srcRect.width(), srcRect.height(), surfaceColorType, bufferColorType, transferBuffer, offset); } void GrDawnGpuRTCommandBuffer::onClearStencilClip(const GrFixedClip& clip, bool insideStencilMask) { fPassEncoder.EndPass(); fPassEncoder = beginRenderPass(dawn::LoadOp::Load, dawn::LoadOp::Clear); } void GrDawnGpuRTCommandBuffer::onClear(const GrFixedClip& clip, const SkPMColor4f& color) { fPassEncoder.EndPass(); fPassEncoder = beginRenderPass(dawn::LoadOp::Clear, dawn::LoadOp::Load); } //////////////////////////////////////////////////////////////////////////////// void GrDawnGpuRTCommandBuffer::inlineUpload(GrOpFlushState* state, GrDeferredTextureUploadFn& upload) { SkASSERT(!"unimplemented"); } void GrDawnGpuRTCommandBuffer::copy(GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) { auto s = static_cast(src->asTexture()); auto d = static_cast(fRenderTarget->asTexture()); if (!s || !d) { return; } dawn::Texture srcTex = s->texture(); dawn::Texture dstTex = d->texture(); uint32_t x = srcRect.x(); uint32_t y = srcRect.y(); uint32_t width = srcRect.width(); uint32_t height = srcRect.height(); int rowPitch = GrDawnRoundRowBytes(width * GrBytesPerPixel(src->config())); int sizeInBytes = rowPitch * height; dawn::BufferDescriptor desc; desc.usage = dawn::BufferUsageBit::CopySrc | dawn::BufferUsageBit::CopyDst; desc.size = sizeInBytes; dawn::Buffer buffer = fGpu->device().CreateBuffer(&desc); uint32_t dstX = dstPoint.x(); uint32_t dstY = dstPoint.y(); fPassEncoder.EndPass(); dawn::TextureCopyView srcTextureCopyView; srcTextureCopyView.texture = srcTex; srcTextureCopyView.origin = {x, y, 0}; dawn::TextureCopyView dstTextureCopyView; dstTextureCopyView.texture = dstTex; dstTextureCopyView.origin = {dstX, dstY, 0}; dawn::BufferCopyView bufferCopyView; bufferCopyView.buffer = buffer; bufferCopyView.offset = 0; bufferCopyView.rowPitch = rowPitch; bufferCopyView.imageHeight = height; dawn::Extent3D copySize = {width, height, 1}; fEncoder.CopyTextureToBuffer(&srcTextureCopyView, &bufferCopyView, ©Size); fEncoder.CopyBufferToTexture(&bufferCopyView, &dstTextureCopyView, ©Size); fPassEncoder = beginRenderPass(dawn::LoadOp::Load, dawn::LoadOp::Load); } //////////////////////////////////////////////////////////////////////////////// static dawn::VertexFormat to_dawn_vertex_format(GrVertexAttribType type) { switch (type) { case kFloat_GrVertexAttribType: case kHalf_GrVertexAttribType: return dawn::VertexFormat::Float; case kFloat2_GrVertexAttribType: case kHalf2_GrVertexAttribType: return dawn::VertexFormat::Float2; case kFloat3_GrVertexAttribType: case kHalf3_GrVertexAttribType: return dawn::VertexFormat::Float3; case kFloat4_GrVertexAttribType: case kHalf4_GrVertexAttribType: return dawn::VertexFormat::Float4; case kUShort2_GrVertexAttribType: return dawn::VertexFormat::UShort2; case kInt_GrVertexAttribType: return dawn::VertexFormat::Int; case kUByte4_norm_GrVertexAttribType: return dawn::VertexFormat::UChar4Norm; default: SkASSERT(!"unsupported vertex format"); return dawn::VertexFormat::Float4; } } static dawn::PrimitiveTopology to_dawn_primitive_topology(GrPrimitiveType primitiveType) { switch (primitiveType) { case GrPrimitiveType::kTriangles: return dawn::PrimitiveTopology::TriangleList; case GrPrimitiveType::kTriangleStrip: return dawn::PrimitiveTopology::TriangleStrip; case GrPrimitiveType::kPoints: return dawn::PrimitiveTopology::PointList; case GrPrimitiveType::kLines: return dawn::PrimitiveTopology::LineList; case GrPrimitiveType::kLineStrip: return dawn::PrimitiveTopology::LineStrip; case GrPrimitiveType::kLinesAdjacency: default: SkASSERT(!"unsupported primitive topology"); return dawn::PrimitiveTopology::TriangleList; } } void GrDawnGpuRTCommandBuffer::setScissorState( const GrPipeline& pipeline, const GrPipeline::FixedDynamicState* fixedDynamicState, const GrPipeline::DynamicStateArrays* dynamicStateArrays) { SkIRect rect; if (pipeline.isScissorEnabled()) { constexpr SkIRect kBogusScissor{0, 0, 1, 1}; rect = fixedDynamicState ? fixedDynamicState->fScissorRect : kBogusScissor; if (kBottomLeft_GrSurfaceOrigin == fOrigin) { rect.setXYWH(rect.x(), fRenderTarget->height() - rect.bottom(), rect.width(), rect.height()); } } else { rect = SkIRect::MakeWH(fRenderTarget->width(), fRenderTarget->height()); } fPassEncoder.SetScissorRect(rect.x(), rect.y(), rect.width(), rect.height()); } void GrDawnGpuRTCommandBuffer::applyState(const GrPipeline& pipeline, const GrPrimitiveProcessor& primProc, const GrTextureProxy* const primProcProxies[], const GrPipeline::FixedDynamicState* fixedDynamicState, const GrPipeline::DynamicStateArrays* dynamicStateArrays, const GrPrimitiveType primitiveType, bool hasPoints) { GrProgramDesc desc; GrProgramDesc::Build(&desc, fRenderTarget, primProc, hasPoints, pipeline, fGpu); dawn::TextureFormat colorFormat; SkAssertResult(GrPixelConfigToDawnFormat(fRenderTarget->config(), &colorFormat)); dawn::TextureFormat stencilFormat = dawn::TextureFormat::Depth24PlusStencil8; bool hasDepthStencil = fRenderTarget->renderTargetPriv().getStencilAttachment() != nullptr; sk_sp program = GrDawnProgramBuilder::Build(fGpu, fRenderTarget, fOrigin, pipeline, primProc, primProcProxies, colorFormat, hasDepthStencil, stencilFormat, &desc); SkASSERT(program); program->setData(primProc, fRenderTarget, fOrigin, pipeline); std::vector inputs; std::vector vertexAttributes; if (primProc.numVertexAttributes() > 0) { size_t offset = 0; int i = 0; for (const auto& attrib : primProc.vertexAttributes()) { dawn::VertexAttributeDescriptor attribute; attribute.shaderLocation = i; attribute.offset = offset; attribute.format = to_dawn_vertex_format(attrib.cpuType()); vertexAttributes.push_back(attribute); offset += attrib.sizeAlign4(); i++; } dawn::VertexBufferDescriptor input; input.stride = offset; input.stepMode = dawn::InputStepMode::Vertex; input.attributes = &vertexAttributes.front(); input.attributeCount = vertexAttributes.size(); inputs.push_back(input); } std::vector instanceAttributes; if (primProc.numInstanceAttributes() > 0) { size_t offset = 0; int i = 0; for (const auto& attrib : primProc.instanceAttributes()) { dawn::VertexAttributeDescriptor attribute; attribute.shaderLocation = i; attribute.offset = offset; attribute.format = to_dawn_vertex_format(attrib.cpuType()); instanceAttributes.push_back(attribute); offset += attrib.sizeAlign4(); i++; } dawn::VertexBufferDescriptor input; input.stride = offset; input.stepMode = dawn::InputStepMode::Instance; input.attributes = &instanceAttributes.front(); input.attributeCount = instanceAttributes.size(); inputs.push_back(input); } dawn::VertexInputDescriptor vertexInput; vertexInput.bufferCount = inputs.size(); vertexInput.buffers = &inputs.front(); vertexInput.indexFormat = dawn::IndexFormat::Uint16; dawn::PipelineStageDescriptor vsDesc; vsDesc.module = program->fVSModule; vsDesc.entryPoint = "main"; dawn::PipelineStageDescriptor fsDesc; fsDesc.module = program->fFSModule; fsDesc.entryPoint = "main"; dawn::RasterizationStateDescriptor rastDesc; rastDesc.frontFace = dawn::FrontFace::CW; rastDesc.cullMode = dawn::CullMode::None; rastDesc.depthBias = 0; rastDesc.depthBiasSlopeScale = 0.0f; rastDesc.depthBiasClamp = 0.0f; dawn::RenderPipelineDescriptor rpDesc; rpDesc.layout = program->fPipelineLayout; rpDesc.vertexStage = &vsDesc; rpDesc.fragmentStage = &fsDesc; rpDesc.vertexInput = &vertexInput; rpDesc.rasterizationState = &rastDesc; rpDesc.primitiveTopology = to_dawn_primitive_topology(primitiveType); rpDesc.sampleCount = 1; rpDesc.depthStencilState = hasDepthStencil ? &program->fDepthStencilState : nullptr; rpDesc.colorStateCount = 1; dawn::ColorStateDescriptor* colorStates[] = { &program->fColorState }; rpDesc.colorStates = colorStates; dawn::RenderPipeline renderPipeline = fGpu->device().CreateRenderPipeline(&rpDesc); fPassEncoder.SetPipeline(renderPipeline); fPassEncoder.SetBindGroup(0, program->fBindGroup, 0, nullptr); if (pipeline.isStencilEnabled()) { fPassEncoder.SetStencilReference(pipeline.getUserStencil()->fFront.fRef); } GrXferProcessor::BlendInfo blendInfo = pipeline.getXferProcessor().getBlendInfo(); const float* c = blendInfo.fBlendConstant.vec(); dawn::Color color{c[0], c[1], c[2], c[3]}; fPassEncoder.SetBlendColor(&color); this->setScissorState(pipeline, fixedDynamicState, dynamicStateArrays); } void GrDawnGpuRTCommandBuffer::onDraw(const GrPrimitiveProcessor& primProc, const GrPipeline& pipeline, const GrPipeline::FixedDynamicState* fixedDynamicState, const GrPipeline::DynamicStateArrays* dynamicStateArrays, const GrMesh meshes[], int meshCount, const SkRect& bounds) { if (!meshCount) { return; } bool hasPoints = false; for (int i = 0; i < meshCount; ++i) { if (meshes[i].primitiveType() == GrPrimitiveType::kPoints) { hasPoints = true; } } const GrTextureProxy* const* primProcProxies = nullptr; if (dynamicStateArrays && dynamicStateArrays->fPrimitiveProcessorTextures) { primProcProxies = dynamicStateArrays->fPrimitiveProcessorTextures; } else if (fixedDynamicState) { primProcProxies = fixedDynamicState->fPrimitiveProcessorTextures; } for (int i = 0; i < meshCount; ++i) { applyState(pipeline, primProc, primProcProxies, fixedDynamicState, dynamicStateArrays, meshes[0].primitiveType(), hasPoints); meshes[i].sendToGpu(this); } } void GrDawnGpuRTCommandBuffer::sendInstancedMeshToGpu(GrPrimitiveType, const GrBuffer* vertexBuffer, int vertexCount, int baseVertex, const GrBuffer* instanceBuffer, int instanceCount, int baseInstance) { static const uint64_t vertexBufferOffsets[1] = {0}; dawn::Buffer vb = static_cast(vertexBuffer)->get(); fPassEncoder.SetVertexBuffers(0, 1, &vb, vertexBufferOffsets); fPassEncoder.Draw(vertexCount, 1, baseVertex, baseInstance); fGpu->stats()->incNumDraws(); } void GrDawnGpuRTCommandBuffer::sendIndexedInstancedMeshToGpu(GrPrimitiveType, const GrBuffer* indexBuffer, int indexCount, int baseIndex, const GrBuffer* vertexBuffer, int baseVertex, const GrBuffer* instanceBuffer, int instanceCount, int baseInstance, GrPrimitiveRestart restart) { uint64_t vertexBufferOffsets[1]; vertexBufferOffsets[0] = 0; dawn::Buffer vb = static_cast(vertexBuffer)->get(); dawn::Buffer ib = static_cast(indexBuffer)->get(); fPassEncoder.SetIndexBuffer(ib, 0); fPassEncoder.SetVertexBuffers(0, 1, &vb, vertexBufferOffsets); fPassEncoder.DrawIndexed(indexCount, 1, baseIndex, baseVertex, baseInstance); fGpu->stats()->incNumDraws(); }