| /* |
| * Copyright 2015 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #ifndef GrOpFlushState_DEFINED |
| #define GrOpFlushState_DEFINED |
| |
| #include "GrAppliedClip.h" |
| #include "GrBufferAllocPool.h" |
| #include "SkArenaAlloc.h" |
| #include "ops/GrMeshDrawOp.h" |
| |
| class GrGpu; |
| class GrGpuCommandBuffer; |
| class GrGpuRTCommandBuffer; |
| class GrResourceProvider; |
| |
| /** Tracks the state across all the GrOps (really just the GrDrawOps) in a GrOpList flush. */ |
| class GrOpFlushState { |
| public: |
| GrOpFlushState(GrGpu*, GrResourceProvider*); |
| |
| ~GrOpFlushState() { this->reset(); } |
| |
| /** Inserts an upload to be executed after all ops in the flush prepared their draws but before |
| the draws are executed to the backend 3D API. */ |
| void addASAPUpload(GrDrawOp::DeferredUploadFn&& upload) { |
| fAsapUploads.emplace_back(std::move(upload)); |
| } |
| |
| const GrCaps& caps() const; |
| GrResourceProvider* resourceProvider() const { return fResourceProvider; } |
| |
| /** Has the token been flushed to the backend 3D API. */ |
| bool hasDrawBeenFlushed(GrDrawOpUploadToken token) const { |
| return token.fSequenceNumber <= fLastFlushedToken.fSequenceNumber; |
| } |
| |
| /** Issue a token to an operation that is being enqueued. */ |
| GrDrawOpUploadToken issueDrawToken() { |
| return GrDrawOpUploadToken(++fLastIssuedToken.fSequenceNumber); |
| } |
| |
| /** Call every time a draw that was issued a token is flushed */ |
| void flushToken() { ++fLastFlushedToken.fSequenceNumber; } |
| |
| /** Gets the next draw token that will be issued. */ |
| GrDrawOpUploadToken nextDrawToken() const { |
| return GrDrawOpUploadToken(fLastIssuedToken.fSequenceNumber + 1); |
| } |
| |
| /** The last token flushed to all the way to the backend API. */ |
| GrDrawOpUploadToken nextTokenToFlush() const { |
| return GrDrawOpUploadToken(fLastFlushedToken.fSequenceNumber + 1); |
| } |
| |
| void* makeVertexSpace(size_t vertexSize, int vertexCount, |
| const GrBuffer** buffer, int* startVertex); |
| uint16_t* makeIndexSpace(int indexCount, const GrBuffer** buffer, int* startIndex); |
| |
| void* makeVertexSpaceAtLeast(size_t vertexSize, int minVertexCount, int fallbackVertexCount, |
| const GrBuffer** buffer, int* startVertex, int* actualVertexCount); |
| uint16_t* makeIndexSpaceAtLeast(int minIndexCount, int fallbackIndexCount, |
| const GrBuffer** buffer, int* startIndex, |
| int* actualIndexCount); |
| |
| /** This is called after each op has a chance to prepare its draws and before the draws are |
| issued. */ |
| void preIssueDraws() { |
| fVertexPool.unmap(); |
| fIndexPool.unmap(); |
| int uploadCount = fAsapUploads.count(); |
| |
| for (int i = 0; i < uploadCount; i++) { |
| this->doUpload(fAsapUploads[i]); |
| } |
| fAsapUploads.reset(); |
| } |
| |
| void doUpload(GrDrawOp::DeferredUploadFn&); |
| |
| void putBackIndices(size_t indices) { fIndexPool.putBack(indices * sizeof(uint16_t)); } |
| |
| void putBackVertexSpace(size_t sizeInBytes) { fVertexPool.putBack(sizeInBytes); } |
| |
| GrGpuCommandBuffer* commandBuffer() { return fCommandBuffer; } |
| // Helper function used by Ops that are only called via RenderTargetOpLists |
| GrGpuRTCommandBuffer* rtCommandBuffer(); |
| void setCommandBuffer(GrGpuCommandBuffer* buffer) { fCommandBuffer = buffer; } |
| |
| GrGpu* gpu() { return fGpu; } |
| |
| void reset() { |
| fVertexPool.reset(); |
| fIndexPool.reset(); |
| fPipelines.reset(); |
| } |
| |
| /** Additional data required on a per-op basis when executing GrDrawOps. */ |
| struct DrawOpArgs { |
| GrRenderTarget* renderTarget() const { return fProxy->priv().peekRenderTarget(); } |
| |
| // TODO: do we still need the dst proxy here? |
| GrRenderTargetProxy* fProxy; |
| GrAppliedClip* fAppliedClip; |
| GrXferProcessor::DstProxy fDstProxy; |
| }; |
| |
| void setDrawOpArgs(DrawOpArgs* opArgs) { fOpArgs = opArgs; } |
| |
| const DrawOpArgs& drawOpArgs() const { |
| SkASSERT(fOpArgs); |
| return *fOpArgs; |
| } |
| |
| GrAppliedClip detachAppliedClip() { |
| SkASSERT(fOpArgs); |
| return fOpArgs->fAppliedClip ? std::move(*fOpArgs->fAppliedClip) : GrAppliedClip(); |
| } |
| |
| template <typename... Args> |
| GrPipeline* allocPipeline(Args&&... args) { |
| return fPipelines.make<GrPipeline>(std::forward<Args>(args)...); |
| } |
| |
| private: |
| GrGpu* fGpu; |
| GrResourceProvider* fResourceProvider; |
| GrGpuCommandBuffer* fCommandBuffer; |
| GrVertexBufferAllocPool fVertexPool; |
| GrIndexBufferAllocPool fIndexPool; |
| SkSTArray<4, GrDrawOp::DeferredUploadFn> fAsapUploads; |
| GrDrawOpUploadToken fLastIssuedToken; |
| GrDrawOpUploadToken fLastFlushedToken; |
| DrawOpArgs* fOpArgs; |
| SkArenaAlloc fPipelines{sizeof(GrPipeline) * 100}; |
| }; |
| |
| /** |
| * A word about uploads and tokens: Ops should usually schedule their uploads to occur at the |
| * begining of a frame whenever possible. These are called ASAP uploads. Of course, this requires |
| * that there are no draws that have yet to be flushed that rely on the old texture contents. In |
| * that case the ASAP upload would happen prior to the previous draw causing the draw to read the |
| * new (wrong) texture data. In that case they should schedule an inline upload. |
| * |
| * Ops, in conjunction with helpers such as GrDrawOpAtlas, can use the token system to know |
| * what the most recent draw was that referenced a resource (or portion of a resource). Each draw |
| * is assigned a token. A resource (or portion) can be tagged with the most recent draw's |
| * token. The target provides a facility for testing whether the draw corresponding to the token |
| * has been flushed. If it has not been flushed then the op must perform an inline upload instead. |
| * When scheduling an inline upload the op provides the token of the draw that the upload must occur |
| * before. The upload will then occur between the draw that requires the new data but after the |
| * token that requires the old data. |
| * |
| * TODO: Currently the token/upload interface is spread over GrDrawOp, GrMeshDrawOp, |
| * GrDrawOp::Target, and GrMeshDrawOp::Target. However, the interface at the GrDrawOp level is not |
| * complete and isn't useful. We should push it down to GrMeshDrawOp until it is required at the |
| * GrDrawOp level. |
| */ |
| |
| /** |
| * GrDrawOp instances use this object to allocate space for their geometry and to issue the draws |
| * that render their op. |
| */ |
| class GrDrawOp::Target { |
| public: |
| Target(GrOpFlushState* state, GrDrawOp* op) : fState(state), fOp(op) {} |
| |
| /** Returns the token of the draw that this upload will occur before. */ |
| GrDrawOpUploadToken addInlineUpload(DeferredUploadFn&& upload) { |
| fOp->fInlineUploads.emplace_back(std::move(upload), fState->nextDrawToken()); |
| return fOp->fInlineUploads.back().fUploadBeforeToken; |
| } |
| |
| /** Returns the token of the draw that this upload will occur before. Since ASAP uploads |
| are done first during a flush, this will be the first token since the most recent |
| flush. */ |
| GrDrawOpUploadToken addAsapUpload(DeferredUploadFn&& upload) { |
| fState->addASAPUpload(std::move(upload)); |
| return fState->nextTokenToFlush(); |
| } |
| |
| bool hasDrawBeenFlushed(GrDrawOpUploadToken token) const { |
| return fState->hasDrawBeenFlushed(token); |
| } |
| |
| /** Gets the next draw token that will be issued by this target. This can be used by an op |
| to record that the next draw it issues will use a resource (e.g. texture) while preparing |
| that draw. */ |
| GrDrawOpUploadToken nextDrawToken() const { return fState->nextDrawToken(); } |
| |
| const GrCaps& caps() const { return fState->caps(); } |
| |
| GrResourceProvider* resourceProvider() const { return fState->resourceProvider(); } |
| |
| protected: |
| GrDrawOp* op() { return fOp; } |
| GrOpFlushState* state() { return fState; } |
| const GrOpFlushState* state() const { return fState; } |
| |
| private: |
| GrOpFlushState* fState; |
| GrDrawOp* fOp; |
| }; |
| |
| /** Extension of GrDrawOp::Target for use by GrMeshDrawOp. Adds the ability to create vertex |
| draws. */ |
| class GrMeshDrawOp::Target : public GrDrawOp::Target { |
| public: |
| Target(GrOpFlushState* state, GrMeshDrawOp* op) : INHERITED(state, op) {} |
| |
| void draw(const GrGeometryProcessor* gp, const GrPipeline* pipeline, const GrMesh& mesh); |
| |
| void* makeVertexSpace(size_t vertexSize, int vertexCount, |
| const GrBuffer** buffer, int* startVertex) { |
| return this->state()->makeVertexSpace(vertexSize, vertexCount, buffer, startVertex); |
| } |
| |
| uint16_t* makeIndexSpace(int indexCount, const GrBuffer** buffer, int* startIndex) { |
| return this->state()->makeIndexSpace(indexCount, buffer, startIndex); |
| } |
| |
| void* makeVertexSpaceAtLeast(size_t vertexSize, int minVertexCount, int fallbackVertexCount, |
| const GrBuffer** buffer, int* startVertex, |
| int* actualVertexCount) { |
| return this->state()->makeVertexSpaceAtLeast(vertexSize, minVertexCount, |
| fallbackVertexCount, buffer, startVertex, |
| actualVertexCount); |
| } |
| |
| uint16_t* makeIndexSpaceAtLeast(int minIndexCount, int fallbackIndexCount, |
| const GrBuffer** buffer, int* startIndex, |
| int* actualIndexCount) { |
| return this->state()->makeIndexSpaceAtLeast(minIndexCount, fallbackIndexCount, buffer, |
| startIndex, actualIndexCount); |
| } |
| |
| /** Helpers for ops which over-allocate and then return data to the pool. */ |
| void putBackIndices(int indices) { this->state()->putBackIndices(indices); } |
| void putBackVertices(int vertices, size_t vertexStride) { |
| this->state()->putBackVertexSpace(vertices * vertexStride); |
| } |
| |
| GrRenderTargetProxy* proxy() const { return this->state()->drawOpArgs().fProxy; } |
| |
| const GrAppliedClip* clip() const { return this->state()->drawOpArgs().fAppliedClip; } |
| |
| GrAppliedClip detachAppliedClip() { return this->state()->detachAppliedClip(); } |
| |
| const GrXferProcessor::DstProxy& dstProxy() const { |
| return this->state()->drawOpArgs().fDstProxy; |
| } |
| |
| template <typename... Args> |
| GrPipeline* allocPipeline(Args&&... args) { |
| return this->state()->allocPipeline(std::forward<Args>(args)...); |
| } |
| |
| /** |
| * Helper that makes a pipeline targeting the op's render target that incorporates the op's |
| * GrAppliedClip. |
| * */ |
| GrPipeline* makePipeline(uint32_t pipelineFlags, GrProcessorSet&& processorSet, |
| GrAppliedClip&& clip) { |
| GrPipeline::InitArgs pipelineArgs; |
| pipelineArgs.fFlags = pipelineFlags; |
| pipelineArgs.fProxy = this->proxy(); |
| pipelineArgs.fDstProxy = this->dstProxy(); |
| pipelineArgs.fCaps = &this->caps(); |
| pipelineArgs.fResourceProvider = this->resourceProvider(); |
| return this->allocPipeline(pipelineArgs, std::move(processorSet), std::move(clip)); |
| } |
| |
| private: |
| GrMeshDrawOp* meshDrawOp() { return static_cast<GrMeshDrawOp*>(this->op()); } |
| typedef GrDrawOp::Target INHERITED; |
| }; |
| |
| #endif |