Chromium Code Reviews| Index: src/gpu/GrCmdBuffer.h |
| diff --git a/src/gpu/GrCmdBuffer.h b/src/gpu/GrCmdBuffer.h |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..e4bc3dabd4be7dbea427eb420982cdeeb0db604c |
| --- /dev/null |
| +++ b/src/gpu/GrCmdBuffer.h |
| @@ -0,0 +1,236 @@ |
| +/* |
| + * Copyright 2014 Google Inc. |
| + * |
| + * Use of this source code is governed by a BSD-style license that can be |
| + * found in the LICENSE file. |
| + */ |
| + |
| +#ifndef GrCmdBuffer_DEFINED |
| +#define GrCmdBuffer_DEFINED |
| + |
| +#include "SkTemplates.h" |
| +#include "SkTypes.h" |
| + |
| +class GrDrawTarget; |
| + |
| +/** |
| + * Ordered list of commands for a GrDrawTarget. |
| + * |
| + * The commands are stored contiguously within large blocks of memory, in order avoid |
| + * excessive calls to malloc(). |
| + * |
| + * @param TAlign A type whose size is the desired alignment for command allocations. |
| + * Using long double will always meet alignment requirements, but this may |
| + * be smaller if the largest alignment requirement is know ahead of time. |
| + */ |
| +template<typename TAlign = long double> class GrCmdBuffer : SkNoncopyable { |
|
bsalomon
2014/10/03 15:04:45
Do long doubles require alignment? Wondering if vo
Chris Dalton
2014/10/03 16:16:24
According to this, long double gets aligned at 16
bsalomon
2014/10/03 18:43:03
Interesting. Maybe just have no default?
Chris Dalton
2014/10/03 19:38:40
Sure
Chris Dalton
2014/10/08 19:29:02
Done.
|
| +public: |
| + class Cmd; |
| + class Iter; |
| + |
| + /** |
| + * Create a command buffer |
| + * |
| + * @param initialSizeInBytes The amount of memory reserved by the command buffer |
| + * initially, and after calls to reset(). |
| + */ |
| + GrCmdBuffer(int initialSizeInBytes) |
| + : fHeadBlock(bytes_to_length(initialSizeInBytes)), |
| + fTailBlock(&fHeadBlock), |
| + fBack(0), |
| + fFirstCmd(NULL), |
| + fLastCmd(NULL) {} |
| + |
| + ~GrCmdBuffer() { this->reset(); } |
| + |
| + bool empty() { |
| + SkASSERT((NULL == fFirstCmd) == (NULL == fLastCmd)); |
| + return NULL == fFirstCmd; |
| + } |
| + |
| + Cmd& front() { |
| + SkASSERT(!this->empty()); |
| + return *fFirstCmd; |
| + } |
| + |
| + Cmd& back() { |
| + SkASSERT(!this->empty()); |
| + return *fLastCmd; |
| + } |
| + |
| + /** |
| + * Destruct all commands in the command buffer and reset to empty. |
| + */ |
| + void reset(); |
| + |
| + /** |
| + * Push a command to the back of the buffer, with optional arguments to for its |
| + * constructor. TCmd must be a subclass of GrCmdBuffer::Cmd. |
| + */ |
| + template<typename TCmd> |
| + TCmd& push_back() { return this->push_back_size<TCmd>(sizeof(TCmd)); } |
|
bsalomon
2014/10/03 15:04:45
Instead of all these could we use something like a
Chris Dalton
2014/10/03 16:16:24
I did consider that approach. The only problem is
bsalomon
2014/10/03 18:43:03
In the other instances where we've done this we ju
Chris Dalton
2014/10/03 19:38:40
Ok, this will work now. (The issue used to be that
Chris Dalton
2014/10/08 19:29:02
Done.
|
| + template<typename TCmd, typename TArg1> |
| + TCmd& push_back(const TArg1& a) { return this->push_back_size<TCmd>(sizeof(TCmd), a); } |
| + template<typename TCmd, typename TArg1, typename TArg2> |
| + TCmd& push_back(const TArg1& a, const TArg2& b) { return this->push_back_size<TCmd>(sizeof(TCmd), a, b); } |
| + template<typename TCmd, typename TArg1, typename TArg2, typename TArg3> |
| + TCmd& push_back(const TArg1& a, const TArg2& b, const TArg3& c) { return this->push_back_size<TCmd>(sizeof(TCmd), a, b, c); } |
| + |
| + /** |
| + * Push a variable-size command to the back of the buffer, with optional arguments for |
| + * its constructor. TCmd must be a subclass of GrCmdBuffer::Cmd. |
| + * |
| + * @param sizeInBytes The amount of memory to allocate for the command. This may be |
|
bsalomon
2014/10/03 15:04:45
Wonder whether we should only take the extra paylo
Chris Dalton
2014/10/03 16:16:24
The Cmd base class could also maybe know where to
bsalomon
2014/10/03 18:43:03
Sure but then we need to store ptr. If it's a para
Chris Dalton
2014/10/03 19:38:40
So at execute time, the client code doesn't know w
Chris Dalton
2014/10/08 19:29:02
Left as-is for now, to resemble the way this would
|
| + * larger than sizeof(TCmd) to accomodate variable-length arrays. |
| + */ |
| + template<typename TCmd> |
| + TCmd& push_back_size(int sizeInBytes); |
| + template<typename TCmd, typename TArg1> |
| + TCmd& push_back_size(int sizeInBytes, const TArg1&); |
| + template<typename TCmd, typename TArg1, typename TArg2> |
| + TCmd& push_back_size(int sizeInBytes, const TArg1&, const TArg2&); |
| + template<typename TCmd, typename TArg1, typename TArg2, typename TArg3> |
| + TCmd& push_back_size(int sizeInBytes, const TArg1&, const TArg2&, const TArg3&); |
| + |
| +private: |
| + static int bytes_to_length(int bytes) { return (bytes + sizeof(TAlign) - 1) / sizeof(TAlign); } |
| + |
| + void* push_back_raw(int sizeInBytes); |
| + template<typename T> T& init_back(T* cmd); |
| + |
| + struct Block { |
| + Block(int length) : fLength(length), fBuffer(fLength) {} |
| + const int fLength; |
| + SkAutoTMalloc<TAlign> fBuffer; |
| + SkAutoTDelete<Block> fNext; |
| + }; |
| + Block fHeadBlock; |
| + Block* fTailBlock; |
| + int fBack; |
| + |
| + Cmd* fFirstCmd; |
| + Cmd* fLastCmd; |
| + |
| + friend class Iter; |
| +}; |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| + |
| +template<typename TAlign> |
| +class GrCmdBuffer<TAlign>::Cmd : SkNoncopyable { |
| +public: |
| + Cmd() : fNext(NULL) {} |
| + virtual ~Cmd() {} |
| + virtual void execute(GrDrawTarget*) = 0; |
| + |
| + uint8_t type() const { return fType; } |
| + void resetType(uint8_t type) { fType = type; } |
| + |
| +private: |
| + Cmd* fNext; |
| + uint8_t fType; |
|
bsalomon
2014/10/03 15:04:45
Is the type really just just used to hang the debu
Chris Dalton
2014/10/03 16:16:24
At least for now, concatInstancedDraw() also check
bsalomon
2014/10/03 18:43:03
The GrIODB could just have a bool, fLastCmdWasDraw
Chris Dalton
2014/10/03 19:38:40
Oh, fLastCmdWasDraw, I like that
Chris Dalton
2014/10/08 19:29:02
I ended up leaving fType in the base Cmd class. It
|
| + |
| + friend class GrCmdBuffer; |
| +}; |
| + |
| +template<typename TAlign> |
| +class GrCmdBuffer<TAlign>::Iter { |
| +public: |
| + Iter(GrCmdBuffer& cmdBuffer) : fCmd(NULL), fNext(cmdBuffer.fFirstCmd) {} |
| + |
| + bool next() { |
| + if (NULL == fNext) { |
| + return false; |
| + } |
| + fCmd = fNext; |
| + fNext = fCmd->fNext; |
| + return true; |
| + } |
| + |
| + Cmd* operator->() { |
| + SkASSERT(fCmd); |
| + return fCmd; |
| + } |
| + |
| +private: |
| + Cmd* fCmd; |
| + Cmd* fNext; |
| +}; |
| + |
| +template<typename TAlign> |
| +void GrCmdBuffer<TAlign>::reset() { |
| + Iter iter(*this); |
| + while (iter.next()) { |
| + iter->~Cmd(); |
| + } |
| + fHeadBlock.fNext.free(); |
| + fTailBlock = &fHeadBlock; |
| + fBack = 0; |
| + fFirstCmd = fLastCmd = NULL; |
| +} |
| + |
| +template<typename TAlign> |
| +void* GrCmdBuffer<TAlign>::push_back_raw(int sizeInBytes) { |
| + const int cmdLength = bytes_to_length(sizeInBytes); |
| + |
| + if (fBack + cmdLength > fTailBlock->fLength) { |
| + SkASSERT(NULL == fTailBlock->fNext.get()); |
| + Block* next = SkNEW_ARGS(Block, (SkTMax(2 * fTailBlock->fLength, cmdLength))); |
| + fTailBlock->fNext.reset(next); |
| + fTailBlock = next; |
| + fBack = 0; |
| + } |
| + |
| + void* data = &fTailBlock->fBuffer[fBack]; |
| + fBack += cmdLength; |
| + return data; |
| +} |
| + |
| +template<typename TAlign> |
| +template<typename T> |
| +T& GrCmdBuffer<TAlign>::init_back(T* cmd) { |
| + cmd->fType = T::kCmdType; |
| + if (NULL == fFirstCmd) { |
| + SkASSERT(NULL == fLastCmd); |
| + fFirstCmd = fLastCmd = cmd; |
| + } else { |
| + SkASSERT(NULL != fLastCmd); |
| + fLastCmd->fNext = cmd; |
| + fLastCmd = cmd; |
| + } |
| + return *cmd; |
| +} |
| + |
| +template<typename TAlign> |
| +template<typename TCmd> |
| +TCmd& GrCmdBuffer<TAlign>::push_back_size(int sizeInBytes) { |
| + SkASSERT(sizeInBytes >= (int)sizeof(TCmd)); |
| + TCmd* cmd = SkNEW_PLACEMENT(this->push_back_raw(sizeInBytes), TCmd); |
| + return this->init_back(cmd); |
| +} |
| + |
| +template<typename TAlign> |
| +template<typename TCmd, typename TArg1> |
| +TCmd& GrCmdBuffer<TAlign>::push_back_size(int sizeInBytes, const TArg1& a) { |
| + SkASSERT(sizeInBytes >= (int)sizeof(TCmd)); |
| + TCmd* cmd = SkNEW_PLACEMENT_ARGS(this->push_back_raw(sizeInBytes), TCmd, (a)); |
| + return this->init_back(cmd); |
| +} |
| + |
| +template<typename TAlign> |
| +template<typename TCmd, typename TArg1, typename TArg2> |
| +TCmd& GrCmdBuffer<TAlign>::push_back_size(int sizeInBytes, const TArg1& a, const TArg2& b) { |
| + SkASSERT(sizeInBytes >= (int)sizeof(TCmd)); |
| + TCmd* cmd = SkNEW_PLACEMENT_ARGS(this->push_back_raw(sizeInBytes), TCmd, (a, b)); |
| + return this->init_back(cmd); |
| +} |
| + |
| +template<typename TAlign> |
| +template<typename TCmd, typename TArg1, typename TArg2, typename TArg3> |
| +TCmd& GrCmdBuffer<TAlign>::push_back_size(int sizeInBytes, const TArg1& a, const TArg2& b, const TArg3& c) { |
| + SkASSERT(sizeInBytes >= (int)sizeof(TCmd)); |
| + TCmd* cmd = SkNEW_PLACEMENT_ARGS(this->push_back_raw(sizeInBytes), TCmd, (a, b, c)); |
| + return this->init_back(cmd); |
| +} |
| + |
| +#endif |