diff options
-rw-r--r-- | gn/tests.gni | 1 | ||||
-rw-r--r-- | src/gpu/GrGpuCommandBuffer.cpp | 3 | ||||
-rw-r--r-- | src/gpu/GrGpuCommandBuffer.h | 5 | ||||
-rw-r--r-- | src/gpu/GrPipeline.cpp | 22 | ||||
-rw-r--r-- | src/gpu/GrPipeline.h | 21 | ||||
-rw-r--r-- | src/gpu/gl/GrGLGpu.cpp | 9 | ||||
-rw-r--r-- | src/gpu/gl/GrGLGpu.h | 3 | ||||
-rw-r--r-- | src/gpu/gl/GrGLGpuCommandBuffer.h | 5 | ||||
-rw-r--r-- | src/gpu/ops/GrMeshDrawOp.cpp | 3 | ||||
-rw-r--r-- | src/gpu/vk/GrVkGpuCommandBuffer.cpp | 35 | ||||
-rw-r--r-- | src/gpu/vk/GrVkGpuCommandBuffer.h | 6 | ||||
-rw-r--r-- | src/gpu/vk/GrVkPipeline.cpp | 76 | ||||
-rw-r--r-- | src/gpu/vk/GrVkPipeline.h | 9 | ||||
-rw-r--r-- | tests/GpuSampleLocationsTest.cpp | 14 | ||||
-rw-r--r-- | tests/GrMeshTest.cpp | 4 | ||||
-rw-r--r-- | tests/GrPipelineDynamicStateTest.cpp | 219 |
16 files changed, 351 insertions, 84 deletions
diff --git a/gn/tests.gni b/gn/tests.gni index 0263dd6249..1b438f44ce 100644 --- a/gn/tests.gni +++ b/gn/tests.gni @@ -92,6 +92,7 @@ tests_sources = [ "$_tests/GrDrawTargetTest.cpp", "$_tests/GrMemoryPoolTest.cpp", "$_tests/GrMeshTest.cpp", + "$_tests/GrPipelineDynamicStateTest.cpp", "$_tests/GrPorterDuffTest.cpp", "$_tests/GrShapeTest.cpp", "$_tests/GrSKSLPrettyPrintTest.cpp", diff --git a/src/gpu/GrGpuCommandBuffer.cpp b/src/gpu/GrGpuCommandBuffer.cpp index 5570a5a65e..9659ca3b9a 100644 --- a/src/gpu/GrGpuCommandBuffer.cpp +++ b/src/gpu/GrGpuCommandBuffer.cpp @@ -39,6 +39,7 @@ void GrGpuCommandBuffer::clearStencilClip(GrRenderTarget* rt, const GrFixedClip& bool GrGpuCommandBuffer::draw(const GrPipeline& pipeline, const GrPrimitiveProcessor& primProc, const GrMesh meshes[], + const GrPipeline::DynamicState dynamicStates[], int meshCount, const SkRect& bounds) { #ifdef SK_DEBUG @@ -58,7 +59,7 @@ bool GrGpuCommandBuffer::draw(const GrPipeline& pipeline, this->gpu()->stats()->incNumFailedDraws(); return false; } - this->onDraw(pipeline, primProc, meshes, meshCount, bounds); + this->onDraw(pipeline, primProc, meshes, dynamicStates, meshCount, bounds); return true; } diff --git a/src/gpu/GrGpuCommandBuffer.h b/src/gpu/GrGpuCommandBuffer.h index fb5dc00943..3a6a3e7005 100644 --- a/src/gpu/GrGpuCommandBuffer.h +++ b/src/gpu/GrGpuCommandBuffer.h @@ -9,6 +9,7 @@ #define GrGpuCommandBuffer_DEFINED #include "GrColor.h" +#include "GrPipeline.h" #include "ops/GrDrawOp.h" class GrOpFlushState; @@ -69,6 +70,7 @@ public: bool draw(const GrPipeline&, const GrPrimitiveProcessor&, const GrMesh[], + const GrPipeline::DynamicState[], int meshCount, const SkRect& bounds); @@ -99,7 +101,8 @@ private: // overridden by backend-specific derived class to perform the draw call. virtual void onDraw(const GrPipeline&, const GrPrimitiveProcessor&, - const GrMesh*, + const GrMesh[], + const GrPipeline::DynamicState[], int meshCount, const SkRect& bounds) = 0; diff --git a/src/gpu/GrPipeline.cpp b/src/gpu/GrPipeline.cpp index 8d1b926be1..9d8b2ef984 100644 --- a/src/gpu/GrPipeline.cpp +++ b/src/gpu/GrPipeline.cpp @@ -112,15 +112,19 @@ void GrPipeline::addDependenciesTo(GrRenderTargetProxy* rtp) const { } } -GrPipeline::GrPipeline(GrRenderTarget* rt, SkBlendMode blendmode) - : fRenderTarget(rt) - , fScissorState() - , fWindowRectsState() - , fUserStencilSettings(&GrUserStencilSettings::kUnused) - , fFlags() - , fXferProcessor(GrPorterDuffXPFactory::MakeNoCoverageXP(blendmode)) - , fFragmentProcessors() - , fNumColorProcessors(0) {} +GrPipeline::GrPipeline(GrRenderTarget* rt, ScissorState scissorState, SkBlendMode blendmode) + : fRenderTarget(rt) + , fScissorState() + , fWindowRectsState() + , fUserStencilSettings(&GrUserStencilSettings::kUnused) + , fFlags() + , fXferProcessor(GrPorterDuffXPFactory::MakeNoCoverageXP(blendmode)) + , fFragmentProcessors() + , fNumColorProcessors(0) { + if (ScissorState::kEnabled == scissorState) { + fScissorState.set({0, 0, 0, 0}); // caller will use the DynamicState struct. + } +} //////////////////////////////////////////////////////////////////////////////// diff --git a/src/gpu/GrPipeline.h b/src/gpu/GrPipeline.h index 078cb68437..3fd518985d 100644 --- a/src/gpu/GrPipeline.h +++ b/src/gpu/GrPipeline.h @@ -68,6 +68,11 @@ public: return flags; } + enum ScissorState : bool { + kEnabled = true, + kDisabled = false + }; + struct InitArgs { uint32_t fFlags = 0; const GrProcessorSet* fProcessors = nullptr; // Must be finalized @@ -80,16 +85,26 @@ public: }; /** + * Graphics state that can change dynamically without creating a new pipeline. + **/ + struct DynamicState { + // Overrides the scissor rectangle (if scissor is enabled in the pipeline). + // TODO: eventually this should be the only way to specify a scissor rectangle, as is the + // case with the simple constructor. + SkIRect fScissorRect; + }; + + /** * A Default constructed pipeline is unusable until init() is called. **/ GrPipeline() = default; /** * Creates a simple pipeline with default settings and no processors. The provided blend mode - * must be "Porter Duff" (<= kLastCoeffMode). This pipeline is initialized without requiring - * a call to init(). + * must be "Porter Duff" (<= kLastCoeffMode). If using ScissorState::kEnabled, the caller must + * specify a scissor rectangle through the DynamicState struct. **/ - GrPipeline(GrRenderTarget*, SkBlendMode); + GrPipeline(GrRenderTarget*, ScissorState, SkBlendMode); GrPipeline(const InitArgs& args) { this->init(args); } diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp index b6c807656f..50c0edba45 100644 --- a/src/gpu/gl/GrGLGpu.cpp +++ b/src/gpu/gl/GrGLGpu.cpp @@ -2449,6 +2449,7 @@ GrGLenum gPrimitiveType2GLMode[] = { void GrGLGpu::draw(const GrPipeline& pipeline, const GrPrimitiveProcessor& primProc, const GrMesh meshes[], + const GrPipeline::DynamicState dynamicStates[], int meshCount) { this->handleDirtyContext(); @@ -2468,6 +2469,14 @@ void GrGLGpu::draw(const GrPipeline& pipeline, this->xferBarrier(pipeline.getRenderTarget(), barrierType); } + if (dynamicStates) { + if (pipeline.getScissorState().enabled()) { + GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(pipeline.getRenderTarget()); + this->flushScissor(dynamicStates[i].fScissorRect, + glRT->getViewport(), glRT->origin()); + } + } + meshes[i].sendToGpu(primProc, this); } diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h index 1785eae143..639b589f50 100644 --- a/src/gpu/gl/GrGLGpu.h +++ b/src/gpu/gl/GrGLGpu.h @@ -101,7 +101,8 @@ public: // on GrGLGpuCommandBuffer. void draw(const GrPipeline&, const GrPrimitiveProcessor&, - const GrMesh*, + const GrMesh[], + const GrPipeline::DynamicState[], int meshCount); diff --git a/src/gpu/gl/GrGLGpuCommandBuffer.h b/src/gpu/gl/GrGLGpuCommandBuffer.h index c7c76a4a9c..c6e7208f4c 100644 --- a/src/gpu/gl/GrGLGpuCommandBuffer.h +++ b/src/gpu/gl/GrGLGpuCommandBuffer.h @@ -49,7 +49,8 @@ private: void onDraw(const GrPipeline& pipeline, const GrPrimitiveProcessor& primProc, - const GrMesh* mesh, + const GrMesh mesh[], + const GrPipeline::DynamicState dynamicStates[], int meshCount, const SkRect& bounds) override { GrGLRenderTarget* target = static_cast<GrGLRenderTarget*>(pipeline.getRenderTarget()); @@ -57,7 +58,7 @@ private: fRenderTarget = target; } SkASSERT(target == fRenderTarget); - fGpu->draw(pipeline, primProc, mesh, meshCount); + fGpu->draw(pipeline, primProc, mesh, dynamicStates, meshCount); } void onClear(GrRenderTarget* rt, const GrFixedClip& clip, GrColor color) override { diff --git a/src/gpu/ops/GrMeshDrawOp.cpp b/src/gpu/ops/GrMeshDrawOp.cpp index d6c2ced38a..d1367e9c9c 100644 --- a/src/gpu/ops/GrMeshDrawOp.cpp +++ b/src/gpu/ops/GrMeshDrawOp.cpp @@ -74,7 +74,8 @@ void GrMeshDrawOp::onExecute(GrOpFlushState* state) { const QueuedDraw& draw = fQueuedDraws[currDrawIdx]; SkASSERT(draw.fPipeline->getRenderTarget() == state->drawOpArgs().fRenderTarget); state->commandBuffer()->draw(*draw.fPipeline, *draw.fGeometryProcessor.get(), - fMeshes.begin() + currMeshIdx, draw.fMeshCnt, this->bounds()); + fMeshes.begin() + currMeshIdx, nullptr, draw.fMeshCnt, + this->bounds()); currMeshIdx += draw.fMeshCnt; state->flushToken(); } diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.cpp b/src/gpu/vk/GrVkGpuCommandBuffer.cpp index 6f39cfa545..6e9c292f04 100644 --- a/src/gpu/vk/GrVkGpuCommandBuffer.cpp +++ b/src/gpu/vk/GrVkGpuCommandBuffer.cpp @@ -468,7 +468,8 @@ void GrVkGpuCommandBuffer::bindGeometry(const GrPrimitiveProcessor& primProc, sk_sp<GrVkPipelineState> GrVkGpuCommandBuffer::prepareDrawState( const GrPipeline& pipeline, const GrPrimitiveProcessor& primProc, - GrPrimitiveType primitiveType) { + GrPrimitiveType primitiveType, + bool hasDynamicState) { CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo]; SkASSERT(cbInfo.fRenderPass); @@ -492,7 +493,18 @@ sk_sp<GrVkPipelineState> GrVkGpuCommandBuffer::prepareDrawState( pipelineState->bind(fGpu, cbInfo.currentCmdBuf()); - GrVkPipeline::SetDynamicState(fGpu, cbInfo.currentCmdBuf(), pipeline); + GrRenderTarget* rt = pipeline.getRenderTarget(); + + if (!pipeline.getScissorState().enabled()) { + GrVkPipeline::SetDynamicScissorRectState(fGpu, cbInfo.currentCmdBuf(), rt, + SkIRect::MakeWH(rt->width(), rt->height())); + } else if (!hasDynamicState) { + GrVkPipeline::SetDynamicScissorRectState(fGpu, cbInfo.currentCmdBuf(), rt, + pipeline.getScissorState().rect()); + } + GrVkPipeline::SetDynamicViewportState(fGpu, cbInfo.currentCmdBuf(), rt); + GrVkPipeline::SetDynamicBlendConstantState(fGpu, cbInfo.currentCmdBuf(), rt->config(), + pipeline.getXferProcessor()); return pipelineState; } @@ -533,7 +545,8 @@ static void prepare_sampled_images(const GrResourceIOProcessor& processor, GrVkG void GrVkGpuCommandBuffer::onDraw(const GrPipeline& pipeline, const GrPrimitiveProcessor& primProc, - const GrMesh* meshes, + const GrMesh meshes[], + const GrPipeline::DynamicState dynamicStates[], int meshCount, const SkRect& bounds) { GrVkRenderTarget* target = static_cast<GrVkRenderTarget*>(pipeline.getRenderTarget()); @@ -557,11 +570,14 @@ void GrVkGpuCommandBuffer::onDraw(const GrPipeline& pipeline, GrPrimitiveType primitiveType = meshes[0].primitiveType(); sk_sp<GrVkPipelineState> pipelineState = this->prepareDrawState(pipeline, primProc, - primitiveType); + primitiveType, + SkToBool(dynamicStates)); if (!pipelineState) { return; } + CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo]; + for (int i = 0; i < meshCount; ++i) { const GrMesh& mesh = meshes[i]; if (mesh.primitiveType() != primitiveType) { @@ -573,17 +589,24 @@ void GrVkGpuCommandBuffer::onDraw(const GrPipeline& pipeline, primitiveType = mesh.primitiveType(); pipelineState = this->prepareDrawState(pipeline, primProc, - primitiveType); + primitiveType, + SkToBool(dynamicStates)); if (!pipelineState) { return; } } + if (dynamicStates) { + if (pipeline.getScissorState().enabled()) { + GrVkPipeline::SetDynamicScissorRectState(fGpu, cbInfo.currentCmdBuf(), + target, dynamicStates[i].fScissorRect); + } + } + SkASSERT(pipelineState); mesh.sendToGpu(primProc, this); } - CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo]; cbInfo.fBounds.join(bounds); cbInfo.fIsEmpty = false; diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.h b/src/gpu/vk/GrVkGpuCommandBuffer.h index 6836fac0ca..9ef166e7db 100644 --- a/src/gpu/vk/GrVkGpuCommandBuffer.h +++ b/src/gpu/vk/GrVkGpuCommandBuffer.h @@ -53,11 +53,13 @@ private: sk_sp<GrVkPipelineState> prepareDrawState(const GrPipeline&, const GrPrimitiveProcessor&, - GrPrimitiveType); + GrPrimitiveType, + bool hasDynamicState); void onDraw(const GrPipeline& pipeline, const GrPrimitiveProcessor& primProc, - const GrMesh* mesh, + const GrMesh mesh[], + const GrPipeline::DynamicState[], int meshCount, const SkRect& bounds) override; diff --git a/src/gpu/vk/GrVkPipeline.cpp b/src/gpu/vk/GrVkPipeline.cpp index 2732c6fc80..0b4289edec 100644 --- a/src/gpu/vk/GrVkPipeline.cpp +++ b/src/gpu/vk/GrVkPipeline.cpp @@ -481,65 +481,56 @@ void GrVkPipeline::freeGPUData(const GrVkGpu* gpu) const { GR_VK_CALL(gpu->vkInterface(), DestroyPipeline(gpu->device(), fPipeline, nullptr)); } -static void set_dynamic_scissor_state(GrVkGpu* gpu, - GrVkCommandBuffer* cmdBuffer, - const GrPipeline& pipeline, - const GrRenderTarget& target) { - // We always use one scissor and if it is disabled we just make it the size of the RT - const GrScissorState& scissorState = pipeline.getScissorState(); - VkRect2D scissor; - if (scissorState.enabled() && - !scissorState.rect().contains(0, 0, target.width(), target.height())) { - // This all assumes the scissorState has previously been clipped to the device space render - // target. - scissor.offset.x = SkTMax(scissorState.rect().fLeft, 0); - scissor.extent.width = scissorState.rect().width(); - if (kTopLeft_GrSurfaceOrigin == target.origin()) { - scissor.offset.y = scissorState.rect().fTop; - } else { - SkASSERT(kBottomLeft_GrSurfaceOrigin == target.origin()); - scissor.offset.y = target.height() - scissorState.rect().fBottom; - } - scissor.offset.y = SkTMax(scissor.offset.y, 0); - scissor.extent.height = scissorState.rect().height(); +void GrVkPipeline::SetDynamicScissorRectState(GrVkGpu* gpu, + GrVkCommandBuffer* cmdBuffer, + const GrRenderTarget* renderTarget, + SkIRect scissorRect) { + if (!scissorRect.intersect(SkIRect::MakeWH(renderTarget->width(), renderTarget->height()))) { + scissorRect.setEmpty(); + } - SkASSERT(scissor.offset.x >= 0); - SkASSERT(scissor.offset.y >= 0); + VkRect2D scissor; + scissor.offset.x = scissorRect.fLeft; + scissor.extent.width = scissorRect.width(); + if (kTopLeft_GrSurfaceOrigin == renderTarget->origin()) { + scissor.offset.y = scissorRect.fTop; } else { - scissor.extent.width = target.width(); - scissor.extent.height = target.height(); - scissor.offset.x = 0; - scissor.offset.y = 0; + SkASSERT(kBottomLeft_GrSurfaceOrigin == renderTarget->origin()); + scissor.offset.y = renderTarget->height() - scissorRect.fBottom; } + scissor.extent.height = scissorRect.height(); + + SkASSERT(scissor.offset.x >= 0); + SkASSERT(scissor.offset.y >= 0); cmdBuffer->setScissor(gpu, 0, 1, &scissor); } -static void set_dynamic_viewport_state(GrVkGpu* gpu, - GrVkCommandBuffer* cmdBuffer, - const GrRenderTarget& target) { +void GrVkPipeline::SetDynamicViewportState(GrVkGpu* gpu, + GrVkCommandBuffer* cmdBuffer, + const GrRenderTarget* renderTarget) { // We always use one viewport the size of the RT VkViewport viewport; viewport.x = 0.0f; viewport.y = 0.0f; - viewport.width = SkIntToScalar(target.width()); - viewport.height = SkIntToScalar(target.height()); + viewport.width = SkIntToScalar(renderTarget->width()); + viewport.height = SkIntToScalar(renderTarget->height()); viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; cmdBuffer->setViewport(gpu, 0, 1, &viewport); } -static void set_dynamic_blend_constant_state(GrVkGpu* gpu, - GrVkCommandBuffer* cmdBuffer, - const GrPipeline& pipeline) { +void GrVkPipeline::SetDynamicBlendConstantState(GrVkGpu* gpu, + GrVkCommandBuffer* cmdBuffer, + GrPixelConfig pixelConfig, + const GrXferProcessor& xferProcessor) { GrXferProcessor::BlendInfo blendInfo; - pipeline.getXferProcessor().getBlendInfo(&blendInfo); + xferProcessor.getBlendInfo(&blendInfo); GrBlendCoeff srcCoeff = blendInfo.fSrcBlend; GrBlendCoeff dstCoeff = blendInfo.fDstBlend; float floatColors[4]; if (blend_coeff_refs_constant(srcCoeff) || blend_coeff_refs_constant(dstCoeff)) { // Swizzle the blend to match what the shader will output. - const GrSwizzle& swizzle = gpu->caps()->shaderCaps()->configOutputSwizzle( - pipeline.getRenderTarget()->config()); + const GrSwizzle& swizzle = gpu->caps()->shaderCaps()->configOutputSwizzle(pixelConfig); GrColor blendConst = swizzle.applyTo(blendInfo.fBlendConstant); GrColorToRGBAFloat(blendConst, floatColors); } else { @@ -547,12 +538,3 @@ static void set_dynamic_blend_constant_state(GrVkGpu* gpu, } cmdBuffer->setBlendConstants(gpu, floatColors); } - -void GrVkPipeline::SetDynamicState(GrVkGpu* gpu, - GrVkCommandBuffer* cmdBuffer, - const GrPipeline& pipeline) { - const GrRenderTarget& target = *pipeline.getRenderTarget(); - set_dynamic_scissor_state(gpu, cmdBuffer, pipeline, target); - set_dynamic_viewport_state(gpu, cmdBuffer, target); - set_dynamic_blend_constant_state(gpu, cmdBuffer, pipeline); -} diff --git a/src/gpu/vk/GrVkPipeline.h b/src/gpu/vk/GrVkPipeline.h index 585014e53c..d05974b102 100644 --- a/src/gpu/vk/GrVkPipeline.h +++ b/src/gpu/vk/GrVkPipeline.h @@ -16,10 +16,13 @@ class GrPipeline; class GrPrimitiveProcessor; +class GrRenderTarget; +class GrXferProcessor; class GrStencilSettings; class GrVkCommandBuffer; class GrVkGpu; class GrVkRenderPass; +struct SkIRect; class GrVkPipeline : public GrVkResource { public: @@ -36,7 +39,11 @@ public: VkPipeline pipeline() const { return fPipeline; } - static void SetDynamicState(GrVkGpu*, GrVkCommandBuffer*, const GrPipeline&); + static void SetDynamicScissorRectState(GrVkGpu*, GrVkCommandBuffer*, const GrRenderTarget*, + SkIRect); + static void SetDynamicViewportState(GrVkGpu*, GrVkCommandBuffer*, const GrRenderTarget*); + static void SetDynamicBlendConstantState(GrVkGpu*, GrVkCommandBuffer*, GrPixelConfig, + const GrXferProcessor&); #ifdef SK_TRACE_VK_RESOURCES void dumpInfo() const override { diff --git a/tests/GpuSampleLocationsTest.cpp b/tests/GpuSampleLocationsTest.cpp index 75d2846dd6..aaab98adda 100644 --- a/tests/GpuSampleLocationsTest.cpp +++ b/tests/GpuSampleLocationsTest.cpp @@ -91,10 +91,6 @@ public: virtual ~TestSampleLocationsInterface() {} }; -static sk_sp<GrPipeline> construct_dummy_pipeline(GrRenderTargetContext* dc) { - return sk_sp<GrPipeline>(new GrPipeline(dc->accessRenderTarget(), SkBlendMode::kSrcOver)); -} - void assert_equal(skiatest::Reporter* reporter, const SamplePattern& pattern, const GrGpu::MultisampleSpecs& specs, bool flipY) { GrAlwaysAssert(specs.fSampleLocations); @@ -136,11 +132,13 @@ void test_sampleLocations(skiatest::Reporter* reporter, TestSampleLocationsInter for (int repeat = 0; repeat < 2; ++repeat) { for (int i = 0; i < numTestPatterns; ++i) { testInterface->overrideSamplePattern(kTestPatterns[i]); - for (GrRenderTargetContext* dc : {bottomUps[i].get(), topDowns[i].get()}) { - sk_sp<GrPipeline> dummyPipeline = construct_dummy_pipeline(dc); - GrRenderTarget* rt = dc->accessRenderTarget(); + for (GrRenderTargetContext* rtc : {bottomUps[i].get(), topDowns[i].get()}) { + GrPipeline dummyPipeline(rtc->accessRenderTarget(), + GrPipeline::ScissorState::kDisabled, + SkBlendMode::kSrcOver); + GrRenderTarget* rt = rtc->accessRenderTarget(); assert_equal(reporter, kTestPatterns[i], - rt->renderTargetPriv().getMultisampleSpecs(*dummyPipeline), + rt->renderTargetPriv().getMultisampleSpecs(dummyPipeline), kBottomLeft_GrSurfaceOrigin == rt->origin()); } } diff --git a/tests/GrMeshTest.cpp b/tests/GrMeshTest.cpp index 3a2be784a5..dd2a2d9e33 100644 --- a/tests/GrMeshTest.cpp +++ b/tests/GrMeshTest.cpp @@ -364,9 +364,9 @@ sk_sp<const GrBuffer> DrawMeshHelper::getIndexBuffer() { void DrawMeshHelper::drawMesh(const GrMesh& mesh) { GrRenderTarget* rt = fState->drawOpArgs().fRenderTarget; - GrPipeline pipeline(rt, SkBlendMode::kSrc); + GrPipeline pipeline(rt, GrPipeline::ScissorState::kDisabled, SkBlendMode::kSrc); GrMeshTestProcessor mtp(mesh.isInstanced(), mesh.hasVertexData()); - fState->commandBuffer()->draw(pipeline, mtp, &mesh, 1, + fState->commandBuffer()->draw(pipeline, mtp, &mesh, nullptr, 1, SkRect::MakeIWH(kImageWidth, kImageHeight)); } diff --git a/tests/GrPipelineDynamicStateTest.cpp b/tests/GrPipelineDynamicStateTest.cpp new file mode 100644 index 0000000000..4c5843aeb1 --- /dev/null +++ b/tests/GrPipelineDynamicStateTest.cpp @@ -0,0 +1,219 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTypes.h" +#include "Test.h" + +#if SK_SUPPORT_GPU + +#include "GrContext.h" +#include "GrColor.h" +#include "GrGeometryProcessor.h" +#include "GrOpFlushState.h" +#include "GrRenderTargetContext.h" +#include "GrRenderTargetContextPriv.h" +#include "GrResourceProvider.h" +#include "SkMakeUnique.h" +#include "glsl/GrGLSLVertexShaderBuilder.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "glsl/GrGLSLGeometryProcessor.h" +#include "glsl/GrGLSLVarying.h" + +/** + * This is a GPU-backend specific test for dynamic pipeline state. It draws boxes using dynamic + * scissor rectangles then reads back the result to verify a successful test. + */ + +using ScissorState = GrPipeline::ScissorState; + +static constexpr int kScreenSize = 6; +static constexpr int kNumMeshes = 4; +static constexpr int kScreenSplitX = kScreenSize/2; +static constexpr int kScreenSplitY = kScreenSize/2; + +static const GrPipeline::DynamicState kDynamicStates[kNumMeshes] = { + {SkIRect::MakeLTRB(0, 0, kScreenSplitX, kScreenSplitY)}, + {SkIRect::MakeLTRB(0, kScreenSplitY, kScreenSplitX, kScreenSize)}, + {SkIRect::MakeLTRB(kScreenSplitX, 0, kScreenSize, kScreenSplitY)}, + {SkIRect::MakeLTRB(kScreenSplitX, kScreenSplitY, kScreenSize, kScreenSize)}, +}; + +static const GrColor kMeshColors[kNumMeshes] { + GrColorPackRGBA(255, 0, 0, 255), + GrColorPackRGBA(0, 255, 0, 255), + GrColorPackRGBA(0, 0, 255, 255), + GrColorPackRGBA(0, 0, 0, 255) +}; + +struct Vertex { + float fX; + float fY; + GrColor fColor; +}; + +class GrPipelineDynamicStateTestProcessor : public GrGeometryProcessor { +public: + GrPipelineDynamicStateTestProcessor() + : fVertex(this->addVertexAttrib("vertex", kVec2f_GrVertexAttribType)) + , fColor(this->addVertexAttrib("color", kVec4ub_GrVertexAttribType)) { + this->initClassID<GrPipelineDynamicStateTestProcessor>(); + } + + const char* name() const override { return "GrPipelineDynamicStateTest Processor"; } + + void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {} + + GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final; + +protected: + const Attribute& fVertex; + const Attribute& fColor; + + friend class GLSLPipelineDynamicStateTestProcessor; + typedef GrGeometryProcessor INHERITED; +}; + +class GLSLPipelineDynamicStateTestProcessor : public GrGLSLGeometryProcessor { + void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&, + FPCoordTransformIter&& transformIter) final {} + + void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final { + const GrPipelineDynamicStateTestProcessor& mp = + args.fGP.cast<GrPipelineDynamicStateTestProcessor>(); + + GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; + varyingHandler->emitAttributes(mp); + varyingHandler->addPassThroughAttribute(&mp.fColor, args.fOutputColor); + + GrGLSLVertexBuilder* v = args.fVertBuilder; + v->codeAppendf("vec2 vertex = %s;", mp.fVertex.fName); + gpArgs->fPositionVar.set(kVec2f_GrSLType, "vertex"); + + GrGLSLPPFragmentBuilder* f = args.fFragBuilder; + f->codeAppendf("%s = vec4(1);", args.fOutputCoverage); + } +}; + +GrGLSLPrimitiveProcessor* +GrPipelineDynamicStateTestProcessor::createGLSLInstance(const GrShaderCaps&) const { + return new GLSLPipelineDynamicStateTestProcessor; +} + +class GrPipelineDynamicStateTestOp : public GrDrawOp { +public: + DEFINE_OP_CLASS_ID + + GrPipelineDynamicStateTestOp(ScissorState scissorState, sk_sp<const GrBuffer> vbuff) + : INHERITED(ClassID()) + , fScissorState(scissorState) + , fVertexBuffer(std::move(vbuff)) { + this->setBounds(SkRect::MakeIWH(kScreenSize, kScreenSize), + HasAABloat::kNo, IsZeroArea::kNo); + } + +private: + const char* name() const override { return "GrPipelineDynamicStateTestOp"; } + FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } + bool xpRequiresDstTexture(const GrCaps&, const GrAppliedClip*) override { return false; } + bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; } + void onPrepare(GrOpFlushState*) override {} + void onExecute(GrOpFlushState* state) override { + GrRenderTarget* rt = state->drawOpArgs().fRenderTarget; + GrPipeline pipeline(rt, fScissorState, SkBlendMode::kSrc); + SkSTArray<kNumMeshes, GrMesh> meshes; + for (int i = 0; i < kNumMeshes; ++i) { + GrMesh& mesh = meshes.emplace_back(kTriangleStrip_GrPrimitiveType); + mesh.setNonIndexedNonInstanced(4); + mesh.setVertexData(fVertexBuffer.get(), 4 * i); + } + state->commandBuffer()->draw(pipeline, GrPipelineDynamicStateTestProcessor(), + meshes.begin(), kDynamicStates, 4, + SkRect::MakeIWH(kScreenSize, kScreenSize)); + } + + ScissorState fScissorState; + const sk_sp<const GrBuffer> fVertexBuffer; + + typedef GrDrawOp INHERITED; +}; + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrPipelineDynamicStateTest, reporter, ctxInfo) { + GrContext* const context = ctxInfo.grContext(); + GrResourceProvider* rp = context->resourceProvider(); + + sk_sp<GrRenderTargetContext> rtc( + context->makeDeferredRenderTargetContext(SkBackingFit::kExact, kScreenSize, kScreenSize, + kRGBA_8888_GrPixelConfig, nullptr)); + if (!rtc) { + ERRORF(reporter, "could not create render target context."); + return; + } + + constexpr float d = (float) kScreenSize; + Vertex vdata[kNumMeshes * 4] = { + {0, 0, kMeshColors[0]}, + {0, d, kMeshColors[0]}, + {d, 0, kMeshColors[0]}, + {d, d, kMeshColors[0]}, + + {0, 0, kMeshColors[1]}, + {0, d, kMeshColors[1]}, + {d, 0, kMeshColors[1]}, + {d, d, kMeshColors[1]}, + + {0, 0, kMeshColors[2]}, + {0, d, kMeshColors[2]}, + {d, 0, kMeshColors[2]}, + {d, d, kMeshColors[2]}, + + {0, 0, kMeshColors[3]}, + {0, d, kMeshColors[3]}, + {d, 0, kMeshColors[3]}, + {d, d, kMeshColors[3]} + }; + + sk_sp<const GrBuffer> vbuff(rp->createBuffer(sizeof(vdata), kVertex_GrBufferType, + kDynamic_GrAccessPattern, + GrResourceProvider::kNoPendingIO_Flag | + GrResourceProvider::kRequireGpuMemory_Flag, + vdata)); + if (!vbuff) { + ERRORF(reporter, "vbuff is null."); + return; + } + + uint32_t resultPx[kScreenSize * kScreenSize]; + + for (ScissorState scissorState : {ScissorState::kEnabled, ScissorState::kDisabled}) { + rtc->clear(nullptr, 0xbaaaaaad, true); + rtc->priv().testingOnly_addDrawOp( + skstd::make_unique<GrPipelineDynamicStateTestOp>(scissorState, vbuff)); + rtc->readPixels(SkImageInfo::Make(kScreenSize, kScreenSize, + kRGBA_8888_SkColorType, kPremul_SkAlphaType), + resultPx, 4 * kScreenSize, 0, 0, 0); + for (int y = 0; y < kScreenSize; ++y) { + for (int x = 0; x < kScreenSize; ++x) { + int expectedColorIdx; + if (ScissorState::kEnabled == scissorState) { + expectedColorIdx = (x < kScreenSplitX ? 0 : 2) + (y < kScreenSplitY ? 0 : 1); + } else { + expectedColorIdx = kNumMeshes - 1; + } + uint32_t expected = kMeshColors[expectedColorIdx]; + uint32_t actual = resultPx[y * kScreenSize + x]; + if (expected != actual) { + ERRORF(reporter, "[scissor=%s] pixel (%i,%i): got 0x%x expected 0x%x", + ScissorState::kEnabled == scissorState ? "enabled" : "disabled", x, y, + actual, expected); + return; + } + } + } + } +} + +#endif |