[graphite] Add uniform buffer support to command buffer

Bug: skia:12466
Change-Id: Ie4c1973427e96d84b77f28edb5a4d6f1d53ef8aa
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/463316
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
This commit is contained in:
Jim Van Verth 2021-10-26 10:25:57 -04:00 committed by SkCQ
parent 8ed23eb917
commit c545aa5aa6
7 changed files with 137 additions and 36 deletions

View File

@ -42,6 +42,12 @@ void CommandBuffer::bindRenderPipeline(sk_sp<RenderPipeline> renderPipeline) {
fHasWork = true;
}
void CommandBuffer::bindUniformBuffer(sk_sp<Buffer> uniformBuffer, size_t offset) {
this->onBindUniformBuffer(uniformBuffer.get(), offset);
this->trackResource(std::move(uniformBuffer));
fHasWork = true;
}
static bool check_max_blit_width(int widthInPixels) {
if (widthInPixels > 32767) {
SkASSERT(false); // surfaces should not be this wide anyway

View File

@ -57,6 +57,8 @@ public:
//---------------------------------------------------------------
void bindRenderPipeline(sk_sp<RenderPipeline> renderPipeline);
void bindUniformBuffer(sk_sp<Buffer>, size_t bufferOffset);
void draw(PrimitiveType type, unsigned int vertexStart, unsigned int vertexCount) {
this->onDraw(type, vertexStart, vertexCount);
fHasWork = true;
@ -84,6 +86,9 @@ private:
virtual void onBeginRenderPass(const RenderPassDesc&) = 0;
virtual void onBindRenderPipeline(const RenderPipeline*) = 0;
virtual void onBindUniformBuffer(const Buffer*, size_t bufferOffset) = 0;
virtual void onDraw(PrimitiveType type, unsigned int vertexStart, unsigned int vertexCount) = 0;
virtual void onCopyTextureToBuffer(const Texture*,

View File

@ -28,20 +28,36 @@ public:
return fKey.size() * sizeof(uint32_t);
}
bool operator== (const RenderPipelineDesc& that) const {
bool operator==(const RenderPipelineDesc& that) const {
return this->fKey == that.fKey;
}
bool operator!= (const RenderPipelineDesc& other) const {
bool operator!=(const RenderPipelineDesc& other) const {
return !(*this == other);
}
// TODO: remove this once we have something real working
void setTestingOnlyShaderIndex(int index) {
fTestingOnlyShaderIndex = index;
if (fKey.count() >= 1) {
fKey[0] = index;
} else {
fKey.push_back(index);
}
}
int testingOnlyShaderIndex() const {
return fTestingOnlyShaderIndex;
}
private:
// Estimate of max expected key size
// TODO: flesh this out
inline static constexpr int kPreAllocSize = 1;
SkSTArray<kPreAllocSize, uint32_t, true> fKey;
int fTestingOnlyShaderIndex;
};
} // namespace skgpu

View File

@ -49,6 +49,8 @@ private:
void onBindRenderPipeline(const skgpu::RenderPipeline*) override;
void onBindUniformBuffer(const skgpu::Buffer*, size_t offset) override;
void onDraw(PrimitiveType type, unsigned int vertexStart, unsigned int vertexCount) override;
void onCopyTextureToBuffer(const skgpu::Texture*,

View File

@ -16,6 +16,8 @@
namespace skgpu::mtl {
static constexpr size_t kUniformBufferIndex = 0;
sk_sp<CommandBuffer> CommandBuffer::Make(const Gpu* gpu) {
sk_cfp<id<MTLCommandBuffer>> cmdBuffer;
id<MTLCommandQueue> queue = gpu->queue();
@ -145,6 +147,14 @@ void CommandBuffer::onBindRenderPipeline(const skgpu::RenderPipeline* renderPipe
fActiveRenderCommandEncoder->setRenderPipelineState(pipelineState);
}
void CommandBuffer::onBindUniformBuffer(const skgpu::Buffer* uniformBuffer,
size_t uniformOffset) {
id<MTLBuffer> mtlBuffer = static_cast<const Buffer*>(uniformBuffer)->mtlBuffer();
fActiveRenderCommandEncoder->setVertexBuffer(mtlBuffer, uniformOffset, kUniformBufferIndex);
fActiveRenderCommandEncoder->setFragmentBuffer(mtlBuffer, uniformOffset, kUniformBufferIndex);
}
static MTLPrimitiveType graphite_to_mtl_primitive(PrimitiveType primitiveType) {
const static MTLPrimitiveType mtlPrimitiveType[] {
MTLPrimitiveTypeTriangle,

View File

@ -7,45 +7,83 @@
#include "experimental/graphite/src/mtl/MtlRenderPipeline.h"
#include "experimental/graphite/src/RenderPipelineDesc.h"
#include "experimental/graphite/src/mtl/MtlGpu.h"
#include "experimental/graphite/src/mtl/MtlUtils.h"
#include "include/private/SkSLString.h"
namespace skgpu::mtl {
sk_sp<RenderPipeline> RenderPipeline::Make(const Gpu* gpu, const skgpu::RenderPipelineDesc&) {
static const char* kTestingOnlyShaders[] = {
// clear viewport to blue
"#include <metal_stdlib>\n"
"#include <simd/simd.h>\n"
"using namespace metal;\n"
"\n"
"typedef struct {\n"
" float4 position [[position]];\n"
"} VertexOutput;\n"
"\n"
"vertex VertexOutput vertexMain(uint vertexID [[vertex_id]]) {\n"
" VertexOutput out;\n"
" float2 position = float2(float(vertexID >> 1), float(vertexID & 1));\n"
" out.position.xy = position * 2 - 1;\n"
" out.position.zw = float2(0.0, 1.0);\n"
" return out;\n"
"}\n"
"\n"
"fragment float4 fragmentMain(VertexOutput in [[stage_in]]) {\n"
" return float4(0.0, 0.0, 1.0, 1.0);\n"
"}",
// clear subarea to given color, using uniform buffer
"#include <metal_stdlib>\n"
"#include <simd/simd.h>\n"
"using namespace metal;\n"
"\n"
"typedef struct {\n"
" float4 position [[position]];\n"
"} VertexOutput;\n"
"\n"
"typedef struct {\n"
" float4 uPosXform;\n"
" float4 uColor;\n"
"} UniformData;\n"
"\n"
"vertex VertexOutput vertexMain(constant UniformData& uniforms [[buffer(0)]],\n"
" uint vertexID [[vertex_id]]) {\n"
" VertexOutput out;\n"
" float2 position = float2(float(vertexID >> 1), float(vertexID & 1));\n"
" out.position.xy = position * uniforms.uPosXform.xy + uniforms.uPosXform.zw;\n"
" out.position.zw = float2(0.0, 1.0);\n"
" return out;\n"
"}\n"
"\n"
"fragment float4 fragmentMain(constant UniformData& uniforms [[buffer(0)]],\n"
" VertexOutput in [[stage_in]]) {\n"
" return uniforms.uColor;\n"
"}",
};
static constexpr NSString* kTestingOnlyShaderLabels[] = {
@"Clear viewport to blue",
@"Clear rect with uniforms"
};
sk_sp<RenderPipeline> RenderPipeline::Make(const Gpu* gpu, const skgpu::RenderPipelineDesc& desc) {
sk_cfp<MTLRenderPipelineDescriptor*> psoDescriptor([[MTLRenderPipelineDescriptor alloc] init]);
// Temp pipeline for now that just fills the viewport with blue
int shaderIndex = desc.testingOnlyShaderIndex();
SkSL::String shaderText;
shaderText.append(
"#include <metal_stdlib>\n"
"#include <simd/simd.h>\n"
"using namespace metal;\n"
"\n"
"typedef struct {\n"
" float4 position [[position]];\n"
"} VertexOutput;\n"
"\n"
"vertex VertexOutput vertexMain(uint vertexID [[vertex_id]]) {\n"
" VertexOutput out;\n"
" float2 position = float2(float(vertexID >> 1), float(vertexID & 1));\n"
" out.position.xy = position * 2 - 1;\n"
" out.position.zw = float2(0.0, 1.0);\n"
" return out;\n"
"}\n"
"\n"
"fragment float4 fragmentMain(VertexOutput in [[stage_in]]) {\n"
" return float4(0.0, 0.0, 1.0, 1.0);\n"
"}"
);
shaderText.append(kTestingOnlyShaders[shaderIndex]);
auto metallib = CompileShaderLibrary(gpu, shaderText);
if (!metallib) {
return nullptr;
}
(*psoDescriptor).label = @"testPipeline";
(*psoDescriptor).label = kTestingOnlyShaderLabels[shaderIndex];
(*psoDescriptor).vertexFunction =
[*metallib newFunctionWithName: @"vertexMain"];

View File

@ -29,6 +29,9 @@ using namespace skgpu;
* This is to test the various pieces of the CommandBuffer interface.
*/
DEF_GRAPHITE_TEST_FOR_CONTEXTS(CommandBufferTest, reporter, context) {
constexpr int kTextureWidth = 1024;
constexpr int kTextureHeight = 768;
auto gpu = context->priv().gpu();
REPORTER_ASSERT(reporter, gpu);
@ -37,7 +40,7 @@ DEF_GRAPHITE_TEST_FOR_CONTEXTS(CommandBufferTest, reporter, context) {
#endif
auto commandBuffer = gpu->resourceProvider()->createCommandBuffer();
SkISize textureSize = { 1024, 768 };
SkISize textureSize = { kTextureWidth, kTextureHeight };
#ifdef SK_METAL
skgpu::mtl::TextureInfo mtlTextureInfo = {
1,
@ -59,31 +62,52 @@ DEF_GRAPHITE_TEST_FOR_CONTEXTS(CommandBufferTest, reporter, context) {
renderPassDesc.fColorAttachment.fTexture = texture;
renderPassDesc.fColorAttachment.fLoadOp = LoadOp::kClear;
renderPassDesc.fColorAttachment.fStoreOp = StoreOp::kStore;
renderPassDesc.fClearColor = { 1, 0, 0, 1 };
renderPassDesc.fClearColor = { 1, 0, 0, 1 }; // red
commandBuffer->beginRenderPass(renderPassDesc);
RenderPipelineDesc pipelineDesc;
pipelineDesc.setTestingOnlyShaderIndex(1);
auto renderPipeline = gpu->resourceProvider()->findOrCreateRenderPipeline(pipelineDesc);
commandBuffer->bindRenderPipeline(std::move(renderPipeline));
struct UniformData {
float fPosXform[4];
float fColor[4];
};
sk_sp<Buffer> uniformBuffer = gpu->resourceProvider()->findOrCreateBuffer(
sizeof(UniformData), BufferType::kUniform, PrioritizeGpuReads::kNo);
UniformData* uniforms = (UniformData*)uniformBuffer->map();
uniforms->fPosXform[0] = 2;
uniforms->fPosXform[1] = 2;
uniforms->fPosXform[2] = -1;
uniforms->fPosXform[3] = -1;
uniforms->fColor[0] = 1;
uniforms->fColor[1] = 1;
uniforms->fColor[2] = 0;
uniforms->fColor[3] = 1;
uniformBuffer->unmap();
commandBuffer->bindUniformBuffer(uniformBuffer, 0);
commandBuffer->draw(PrimitiveType::kTriangleStrip, 0, 4);
commandBuffer->endRenderPass();
sk_sp<Buffer> buffer = gpu->resourceProvider()->findOrCreateBuffer(1024*768*4,
BufferType::kXferGpuToCpu,
PrioritizeGpuReads::kNo);
REPORTER_ASSERT(reporter, buffer);
SkIRect srcRect = { 0, 0, 1024, 768 };
size_t rowBytes = 1024*4;
commandBuffer->copyTextureToBuffer(texture, srcRect, buffer, 0, rowBytes);
// TODO: add 4-byte transfer buffer alignment for Mac to Caps
// add bpp to Caps
size_t rowBytes = 4*kTextureWidth;
size_t bufferSize = rowBytes*kTextureHeight;
sk_sp<Buffer> copyBuffer = gpu->resourceProvider()->findOrCreateBuffer(
bufferSize, BufferType::kXferGpuToCpu, PrioritizeGpuReads::kNo);
REPORTER_ASSERT(reporter, copyBuffer);
SkIRect srcRect = { 0, 0, kTextureWidth, kTextureHeight };
commandBuffer->copyTextureToBuffer(texture, srcRect, copyBuffer, 0, rowBytes);
bool result = gpu->submit(commandBuffer);
REPORTER_ASSERT(reporter, result);
gpu->checkForFinishedWork(skgpu::SyncToCpu::kYes);
uint32_t* pixels = (uint32_t*)(buffer->map());
REPORTER_ASSERT(reporter, pixels[0] == 0xffff0000);
uint32_t* pixels = (uint32_t*)(copyBuffer->map());
REPORTER_ASSERT(reporter, pixels[0] == 0xff00ffff);
copyBuffer->unmap();
#if GRAPHITE_TEST_UTILS && CAPTURE_COMMANDBUFFER
gpu->testingOnly_endCapture();