Introduce render passes

* First API design (many features missing, including input attachments)
* Metal implementation (no OpenGL yet)
* Render-to-texture demo (a little broken until we have depth buffers)
* Update examples to use render passes
This commit is contained in:
Kai Ninomiya 2017-05-16 14:04:22 -07:00 committed by Kai Ninomiya
parent ca309db58a
commit 68df8b0a3a
34 changed files with 1305 additions and 54 deletions

View File

@ -22,6 +22,8 @@
nxt::Device device;
nxt::Queue queue;
nxt::Pipeline pipeline;
nxt::RenderPass renderpass;
nxt::Framebuffer framebuffer;
float RandomFloat(float min, float max) {
float zeroOne = rand() / float(RAND_MAX);
@ -103,7 +105,9 @@ void init() {
})"
);
CreateDefaultRenderPass(device, &renderpass, &framebuffer);
pipeline = device.CreatePipelineBuilder()
.SetSubpass(renderpass, 0)
.SetStage(nxt::ShaderStage::Vertex, vsModule, "main")
.SetStage(nxt::ShaderStage::Fragment, fsModule, "main")
.GetResult();
@ -129,6 +133,7 @@ void frame() {
for (int j = 0; j < 50; j++) {
nxt::CommandBufferBuilder builder = device.CreateCommandBufferBuilder()
.BeginRenderPass(renderpass, framebuffer)
.SetPipeline(pipeline)
.Clone();
@ -140,6 +145,7 @@ void frame() {
i++;
}
builder.EndRenderPass();
commands[j] = builder.GetResult();
}

View File

@ -60,6 +60,10 @@ add_executable(HelloCompute HelloCompute.cpp)
target_link_libraries(HelloCompute utils)
SetCXX14(HelloCompute)
add_executable(RenderToTexture RenderToTexture.cpp)
target_link_libraries(RenderToTexture utils)
SetCXX14(RenderToTexture)
add_executable(Animometer Animometer.cpp)
target_link_libraries(Animometer utils)
SetCXX14(Animometer)

View File

@ -28,6 +28,8 @@ nxt::Buffer modelBuffer;
std::array<nxt::Buffer, 2> particleBuffers;
nxt::Pipeline renderPipeline;
nxt::RenderPass renderpass;
nxt::Framebuffer framebuffer;
nxt::Buffer updateParams;
nxt::Pipeline updatePipeline;
@ -134,7 +136,9 @@ void initRender() {
.SetInput(1, sizeof(glm::vec2), nxt::InputStepMode::Vertex)
.GetResult();
CreateDefaultRenderPass(device, &renderpass, &framebuffer);
renderPipeline = device.CreatePipelineBuilder()
.SetSubpass(renderpass, 0)
.SetStage(nxt::ShaderStage::Vertex, vsModule, "main")
.SetStage(nxt::ShaderStage::Fragment, fsModule, "main")
.SetInputState(inputState)
@ -279,11 +283,13 @@ void initCommandBuffers() {
.SetBindGroup(0, updateBGs[i])
.Dispatch(kNumParticles, 1, 1)
.BeginRenderPass(renderpass, framebuffer)
.SetPipeline(renderPipeline)
.TransitionBufferUsage(bufferDst, nxt::BufferUsageBit::Vertex)
.SetVertexBuffers(0, 1, &bufferDst, zeroOffsets)
.SetVertexBuffers(1, 1, &modelBuffer, zeroOffsets)
.DrawArrays(3, kNumParticles, 0, 0)
.EndRenderPass()
.GetResult();
}

View File

@ -22,6 +22,8 @@ nxt::Queue queue;
nxt::Buffer buffer;
nxt::Pipeline renderPipeline;
nxt::BindGroup renderBindGroup;
nxt::RenderPass renderpass;
nxt::Framebuffer framebuffer;
nxt::Pipeline computePipeline;
nxt::BindGroup computeBindGroup;
@ -107,7 +109,9 @@ void init() {
.SetBindGroupLayout(0, bgl)
.GetResult();
CreateDefaultRenderPass(device, &renderpass, &framebuffer);
renderPipeline = device.CreatePipelineBuilder()
.SetSubpass(renderpass, 0)
.SetLayout(pl)
.SetStage(nxt::ShaderStage::Vertex, vsModule, "main")
.SetStage(nxt::ShaderStage::Fragment, fsModule, "main")
@ -128,10 +132,12 @@ void frame() {
.SetBindGroup(0, computeBindGroup)
.Dispatch(1, 1, 1)
.BeginRenderPass(renderpass, framebuffer)
.SetPipeline(renderPipeline)
.TransitionBufferUsage(buffer, nxt::BufferUsageBit::Uniform)
.SetBindGroup(0, renderBindGroup)
.DrawArrays(3, 1, 0, 0)
.EndRenderPass()
.GetResult();

View File

@ -24,6 +24,8 @@ nxt::Buffer vertexBuffer;
nxt::Queue queue;
nxt::Pipeline pipeline;
nxt::RenderPass renderpass;
nxt::Framebuffer framebuffer;
void initBuffers() {
static const uint32_t indexData[3] = {
@ -82,7 +84,9 @@ void init() {
.SetInput(0, 4 * sizeof(float), nxt::InputStepMode::Vertex)
.GetResult();
CreateDefaultRenderPass(device, &renderpass, &framebuffer);
pipeline = device.CreatePipelineBuilder()
.SetSubpass(renderpass, 0)
.SetStage(nxt::ShaderStage::Vertex, vsModule, "main")
.SetStage(nxt::ShaderStage::Fragment, fsModule, "main")
.SetInputState(inputState)
@ -92,10 +96,12 @@ void init() {
void frame() {
static const uint32_t vertexBufferOffsets[1] = {0};
nxt::CommandBuffer commands = device.CreateCommandBufferBuilder()
.BeginRenderPass(renderpass, framebuffer)
.SetPipeline(pipeline)
.SetVertexBuffers(0, 1, &vertexBuffer, vertexBufferOffsets)
.SetIndexBuffer(indexBuffer, 0, nxt::IndexFormat::Uint32)
.DrawElements(3, 1, 0, 0)
.EndRenderPass()
.GetResult();
queue.Submit(1, &commands);

View File

@ -24,6 +24,8 @@ nxt::Buffer instanceBuffer;
nxt::Queue queue;
nxt::Pipeline pipeline;
nxt::RenderPass renderpass;
nxt::Framebuffer framebuffer;
void initBuffers() {
static const float vertexData[12] = {
@ -89,7 +91,9 @@ void init() {
.SetInput(1, 2 * sizeof(float), nxt::InputStepMode::Instance)
.GetResult();
CreateDefaultRenderPass(device, &renderpass, &framebuffer);
pipeline = device.CreatePipelineBuilder()
.SetSubpass(renderpass, 0)
.SetStage(nxt::ShaderStage::Vertex, vsModule, "main")
.SetStage(nxt::ShaderStage::Fragment, fsModule, "main")
.SetInputState(inputState)
@ -99,10 +103,12 @@ void init() {
void frame() {
static const uint32_t vertexBufferOffsets[1] = {0};
nxt::CommandBuffer commands = device.CreateCommandBufferBuilder()
.BeginRenderPass(renderpass, framebuffer)
.SetPipeline(pipeline)
.SetVertexBuffers(0, 1, &vertexBuffer, vertexBufferOffsets)
.SetVertexBuffers(1, 1, &instanceBuffer, vertexBufferOffsets)
.DrawArrays(3, 4, 0, 0)
.EndRenderPass()
.GetResult();
queue.Submit(1, &commands);

View File

@ -19,6 +19,8 @@
nxtDevice device;
nxtQueue queue;
nxtPipeline pipeline;
nxtRenderPass renderpass;
nxtFramebuffer framebuffer;
void init() {
nxtProcTable procs;
@ -47,8 +49,25 @@ void init() {
"}\n";
nxtShaderModule fsModule = CreateShaderModule(device, NXT_SHADER_STAGE_FRAGMENT, fs);
{
nxtRenderPassBuilder builder = nxtDeviceCreateRenderPassBuilder(device);
nxtRenderPassBuilderSetAttachmentCount(builder, 1);
nxtRenderPassBuilderAttachmentSetFormat(builder, 0, NXT_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM);
nxtRenderPassBuilderSetSubpassCount(builder, 1);
nxtRenderPassBuilderSubpassSetColorAttachment(builder, 0, 0, 0);
renderpass = nxtRenderPassBuilderGetResult(builder);
nxtRenderPassBuilderRelease(builder);
}
{
nxtFramebufferBuilder builder = nxtDeviceCreateFramebufferBuilder(device);
nxtFramebufferBuilderSetRenderPass(builder, renderpass);
nxtFramebufferBuilderSetDimensions(builder, 640, 480);
framebuffer = nxtFramebufferBuilderGetResult(builder);
nxtFramebufferBuilderRelease(builder);
}
{
nxtPipelineBuilder builder = nxtDeviceCreatePipelineBuilder(device);
nxtPipelineBuilderSetSubpass(builder, renderpass, 0);
nxtPipelineBuilderSetStage(builder, NXT_SHADER_STAGE_VERTEX, vsModule, "main");
nxtPipelineBuilderSetStage(builder, NXT_SHADER_STAGE_FRAGMENT, fsModule, "main");
pipeline = nxtPipelineBuilderGetResult(builder);
@ -63,8 +82,10 @@ void frame() {
nxtCommandBuffer commands;
{
nxtCommandBufferBuilder builder = nxtDeviceCreateCommandBufferBuilder(device);
nxtCommandBufferBuilderBeginRenderPass(builder, renderpass, framebuffer);
nxtCommandBufferBuilderSetPipeline(builder, pipeline);
nxtCommandBufferBuilderDrawArrays(builder, 3, 1, 0, 0);
nxtCommandBufferBuilderEndRenderPass(builder);
commands = nxtCommandBufferBuilderGetResult(builder);
nxtCommandBufferBuilderRelease(builder);
}

View File

@ -27,6 +27,8 @@ nxt::Sampler sampler;
nxt::Queue queue;
nxt::Pipeline pipeline;
nxt::RenderPass renderpass;
nxt::Framebuffer framebuffer;
nxt::BindGroup bindGroup;
void initBuffers() {
@ -135,7 +137,9 @@ void init() {
.SetBindGroupLayout(0, bgl)
.GetResult();
CreateDefaultRenderPass(device, &renderpass, &framebuffer);
pipeline = device.CreatePipelineBuilder()
.SetSubpass(renderpass, 0)
.SetLayout(pl)
.SetStage(nxt::ShaderStage::Vertex, vsModule, "main")
.SetStage(nxt::ShaderStage::Fragment, fsModule, "main")
@ -159,11 +163,13 @@ void frame() {
if (s.b >= 1.0f) {s.b = 0.0f;}
static const uint32_t vertexBufferOffsets[1] = {0};
nxt::CommandBuffer commands = device.CreateCommandBufferBuilder()
.BeginRenderPass(renderpass, framebuffer)
.SetPipeline(pipeline)
.SetBindGroup(0, bindGroup)
.SetVertexBuffers(0, 1, &vertexBuffer, vertexBufferOffsets)
.SetIndexBuffer(indexBuffer, 0, nxt::IndexFormat::Uint32)
.DrawElements(3, 1, 0, 0)
.EndRenderPass()
.GetResult();
queue.Submit(1, &commands);

View File

@ -19,6 +19,8 @@
nxt::Device device;
nxt::Queue queue;
nxt::Pipeline pipeline;
nxt::RenderPass renderpass;
nxt::Framebuffer framebuffer;
nxt::Buffer buffer;
nxt::BindGroup bindGroup;
@ -59,7 +61,9 @@ void init() {
.SetBindGroupLayout(0, bgl)
.GetResult();
CreateDefaultRenderPass(device, &renderpass, &framebuffer);
pipeline = device.CreatePipelineBuilder()
.SetSubpass(renderpass, 0)
.SetLayout(pl)
.SetStage(nxt::ShaderStage::Vertex, vsModule, "main")
.SetStage(nxt::ShaderStage::Fragment, fsModule, "main")
@ -91,10 +95,12 @@ void frame() {
buffer.SetSubData(0, sizeof(s) / sizeof(uint32_t), reinterpret_cast<uint32_t*>(&s));
nxt::CommandBuffer commands = device.CreateCommandBufferBuilder()
.BeginRenderPass(renderpass, framebuffer)
.SetPipeline(pipeline)
.TransitionBufferUsage(buffer, nxt::BufferUsageBit::Uniform)
.SetBindGroup(0, bindGroup)
.DrawArrays(3, 1, 0, 0)
.EndRenderPass()
.GetResult();
queue.Submit(1, &commands);

View File

@ -23,6 +23,8 @@ nxt::Buffer vertexBuffer;
nxt::Queue queue;
nxt::Pipeline pipeline;
nxt::RenderPass renderpass;
nxt::Framebuffer framebuffer;
void initBuffers() {
static const float vertexData[12] = {
@ -59,7 +61,7 @@ void init() {
nxt::ShaderModule fsModule = CreateShaderModule(device, nxt::ShaderStage::Fragment, R"(
#version 450
out vec4 fragColor;
layout(location = 0) out vec4 fragColor;
void main() {
fragColor = vec4(1.0, 0.0, 0.0, 1.0);
})"
@ -70,7 +72,9 @@ void init() {
.SetInput(0, 4 * sizeof(float), nxt::InputStepMode::Vertex)
.GetResult();
CreateDefaultRenderPass(device, &renderpass, &framebuffer);
pipeline = device.CreatePipelineBuilder()
.SetSubpass(renderpass, 0)
.SetStage(nxt::ShaderStage::Vertex, vsModule, "main")
.SetStage(nxt::ShaderStage::Fragment, fsModule, "main")
.SetInputState(inputState)
@ -80,9 +84,11 @@ void init() {
void frame() {
static const uint32_t vertexBufferOffsets[1] = {0};
nxt::CommandBuffer commands = device.CreateCommandBufferBuilder()
.BeginRenderPass(renderpass, framebuffer)
.SetPipeline(pipeline)
.SetVertexBuffers(0, 1, &vertexBuffer, vertexBufferOffsets)
.DrawArrays(3, 1, 0, 0)
.EndRenderPass()
.GetResult();
queue.Submit(1, &commands);

View File

@ -0,0 +1,235 @@
// Copyright 2017 The NXT Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "Utils.h"
#include <unistd.h>
#include <vector>
nxt::Device device;
nxt::Buffer vertexBuffer;
nxt::Buffer vertexBufferQuad;
nxt::Texture renderTarget;
nxt::TextureView renderTargetView;
nxt::Sampler samplerPost;
nxt::Queue queue;
nxt::Pipeline pipeline;
nxt::Pipeline pipelinePost;
nxt::BindGroup bindGroup;
nxt::RenderPass renderpass;
nxt::Framebuffer framebuffer;
void initBuffers() {
static const float vertexData[12] = {
0.0f, 0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 0.0f, 1.0f,
};
vertexBuffer = device.CreateBufferBuilder()
.SetAllowedUsage(nxt::BufferUsageBit::Mapped | nxt::BufferUsageBit::Vertex)
.SetInitialUsage(nxt::BufferUsageBit::Mapped)
.SetSize(sizeof(vertexData))
.GetResult();
vertexBuffer.SetSubData(0, sizeof(vertexData) / sizeof(uint32_t),
reinterpret_cast<const uint32_t*>(vertexData));
vertexBuffer.FreezeUsage(nxt::BufferUsageBit::Vertex);
static const float vertexDataQuad[24] = {
-1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
};
vertexBufferQuad = device.CreateBufferBuilder()
.SetAllowedUsage(nxt::BufferUsageBit::Mapped | nxt::BufferUsageBit::Vertex)
.SetInitialUsage(nxt::BufferUsageBit::Mapped)
.SetSize(sizeof(vertexDataQuad))
.GetResult();
vertexBufferQuad.SetSubData(0, sizeof(vertexDataQuad) / sizeof(uint32_t),
reinterpret_cast<const uint32_t*>(vertexDataQuad));
vertexBufferQuad.FreezeUsage(nxt::BufferUsageBit::Vertex);
}
void initTextures() {
renderTarget = device.CreateTextureBuilder()
.SetDimension(nxt::TextureDimension::e2D)
.SetExtent(640, 480, 1)
.SetFormat(nxt::TextureFormat::R8G8B8A8Unorm)
.SetMipLevels(1)
.SetAllowedUsage(nxt::TextureUsageBit::ColorAttachment | nxt::TextureUsageBit::Sampled)
.SetInitialUsage(nxt::TextureUsageBit::ColorAttachment)
.GetResult();
renderTargetView = renderTarget.CreateTextureViewBuilder().GetResult();
samplerPost = device.CreateSamplerBuilder()
.SetFilterMode(nxt::FilterMode::Linear, nxt::FilterMode::Linear, nxt::FilterMode::Linear)
.GetResult();
}
void initRenderPass() {
renderpass = device.CreateRenderPassBuilder()
.SetAttachmentCount(2)
.AttachmentSetFormat(0, nxt::TextureFormat::R8G8B8A8Unorm)
.AttachmentSetFormat(1, nxt::TextureFormat::R8G8B8A8Unorm)
.SetSubpassCount(2)
// subpass 0
.SubpassSetColorAttachment(0, 0, 0) // -> render target
// subpass 1
.SubpassSetColorAttachment(1, 0, 1) // -> back buffer
.GetResult();
framebuffer = device.CreateFramebufferBuilder()
.SetRenderPass(renderpass)
// attachment 0 -> render target
.SetAttachment(0, renderTargetView)
// attachment 1 -> back buffer
// (implicit) // TODO(kainino@chromium.org): use the texture provided by WSI
.SetDimensions(640, 480)
.GetResult();
}
void initPipeline() {
nxt::ShaderModule vsModule = CreateShaderModule(device, nxt::ShaderStage::Vertex, R"(
#version 450
layout(location = 0) in vec4 pos;
void main() {
gl_Position = pos;
})"
);
nxt::ShaderModule fsModule = CreateShaderModule(device, nxt::ShaderStage::Fragment, R"(
#version 450
layout(location = 0) out vec4 fragColor;
void main() {
fragColor = vec4(1.0, 0.0, 0.0, 1.0);
})"
);
auto inputState = device.CreateInputStateBuilder()
.SetAttribute(0, 0, nxt::VertexFormat::FloatR32G32B32A32, 0)
.SetInput(0, 4 * sizeof(float), nxt::InputStepMode::Vertex)
.GetResult();
pipeline = device.CreatePipelineBuilder()
.SetSubpass(renderpass, 0)
.SetStage(nxt::ShaderStage::Vertex, vsModule, "main")
.SetStage(nxt::ShaderStage::Fragment, fsModule, "main")
.SetInputState(inputState)
.GetResult();
}
void initPipelinePost() {
nxt::ShaderModule vsModule = CreateShaderModule(device, nxt::ShaderStage::Vertex, R"(
#version 450
layout(location = 0) in vec4 pos;
void main() {
gl_Position = pos;
})"
);
nxt::ShaderModule fsModule = CreateShaderModule(device, nxt::ShaderStage::Fragment, R"(
#version 450
layout(set = 0, binding = 0) uniform sampler samp;
layout(set = 0, binding = 1) uniform texture2D tex;
layout(location = 0) out vec4 fragColor;
void main() {
fragColor = texture(sampler2D(tex, samp), gl_FragCoord.xy / vec2(640.0, 480.0)) + vec4(0.0, 0.0, 0.5, 0.0);
})"
);
auto inputState = device.CreateInputStateBuilder()
.SetAttribute(0, 0, nxt::VertexFormat::FloatR32G32B32A32, 0)
.SetInput(0, 4 * sizeof(float), nxt::InputStepMode::Vertex)
.GetResult();
nxt::BindGroupLayout bgl = device.CreateBindGroupLayoutBuilder()
.SetBindingsType(nxt::ShaderStageBit::Fragment, nxt::BindingType::Sampler, 0, 1)
.SetBindingsType(nxt::ShaderStageBit::Fragment, nxt::BindingType::SampledTexture, 1, 1)
.GetResult();
nxt::PipelineLayout pl = device.CreatePipelineLayoutBuilder()
.SetBindGroupLayout(0, bgl)
.GetResult();
pipelinePost = device.CreatePipelineBuilder()
.SetSubpass(renderpass, 1)
.SetLayout(pl)
.SetStage(nxt::ShaderStage::Vertex, vsModule, "main")
.SetStage(nxt::ShaderStage::Fragment, fsModule, "main")
.SetInputState(inputState)
.GetResult();
bindGroup = device.CreateBindGroupBuilder()
.SetLayout(bgl)
.SetUsage(nxt::BindGroupUsage::Frozen)
.SetSamplers(0, 1, &samplerPost)
.SetTextureViews(1, 1, &renderTargetView)
.GetResult();
}
void init() {
nxtProcTable procs;
GetProcTableAndDevice(&procs, &device);
nxtSetProcs(&procs);
queue = device.CreateQueueBuilder().GetResult();
initBuffers();
initTextures();
initRenderPass();
initPipeline();
initPipelinePost();
}
void frame() {
static const uint32_t vertexBufferOffsets[1] = {0};
nxt::CommandBuffer commands = device.CreateCommandBufferBuilder()
.BeginRenderPass(renderpass, framebuffer)
// renderTarget is not transitioned here because it's implicit in
// BeginRenderPass or AdvanceSubpass.
.SetPipeline(pipeline)
.SetVertexBuffers(0, 1, &vertexBuffer, vertexBufferOffsets)
.DrawArrays(3, 1, 0, 0)
.AdvanceSubpass()
// renderTarget must be transitioned here because it's Sampled, not
// ColorAttachment or InputAttachment.
.TransitionTextureUsage(renderTarget, nxt::TextureUsageBit::Sampled)
.SetPipeline(pipelinePost)
.SetBindGroup(0, bindGroup)
.SetVertexBuffers(0, 1, &vertexBufferQuad, vertexBufferOffsets)
.DrawArrays(6, 1, 0, 0)
.EndRenderPass()
.GetResult();
queue.Submit(1, &commands);
SwapBuffers();
}
int main(int argc, const char* argv[]) {
if (!InitUtils(argc, argv)) {
return 1;
}
init();
while (!ShouldQuit()) {
frame();
usleep(16000);
}
// TODO release stuff
}

View File

@ -200,6 +200,19 @@ nxt::ShaderModule CreateShaderModule(const nxt::Device& device, nxt::ShaderStage
.GetResult();
}
void CreateDefaultRenderPass(const nxt::Device& device, nxt::RenderPass* renderPass, nxt::Framebuffer* framebuffer) {
*renderPass = device.CreateRenderPassBuilder()
.SetAttachmentCount(1)
.AttachmentSetFormat(0, nxt::TextureFormat::R8G8B8A8Unorm)
.SetSubpassCount(1)
.SubpassSetColorAttachment(0, 0, 0)
.GetResult();
*framebuffer = device.CreateFramebufferBuilder()
.SetRenderPass(*renderPass)
.SetDimensions(640, 480)
.GetResult();
}
extern "C" {
bool InitUtils(int argc, const char** argv) {
for (int i = 0; i < argc; i++) {

View File

@ -32,6 +32,7 @@ extern "C" {
#include <nxt/nxtcpp.h>
void GetProcTableAndDevice(nxtProcTable* procs, nxt::Device* device);
nxt::ShaderModule CreateShaderModule(const nxt::Device& device, nxt::ShaderStage stage, const char* source);
void CreateDefaultRenderPass(const nxt::Device& device, nxt::RenderPass* renderPass, nxt::Framebuffer* framebuffer);
#else
void GetProcTableAndDevice(nxtProcTable* procs, nxtDevice* device);
nxtShaderModule CreateShaderModule(nxtDevice device, nxtShaderStage stage, const char* source);

104
next.json
View File

@ -218,6 +218,19 @@
"name": "get result",
"returns": "command buffer"
},
{
"name": "begin render pass",
"args": [
{"name": "render pass", "type": "render pass"},
{"name": "framebuffer", "type": "framebuffer"}
]
},
{
"name": "advance subpass"
},
{
"name": "end render pass"
},
{
"name": "copy buffer to texture",
"args": [
@ -347,6 +360,10 @@
"name": "create command buffer builder",
"returns": "command buffer builder"
},
{
"name": "create framebuffer builder",
"returns": "framebuffer builder"
},
{
"name": "create input state builder",
"returns": "input state builder"
@ -363,6 +380,10 @@
"name": "create queue builder",
"returns": "queue builder"
},
{
"name": "create render pass builder",
"returns": "render pass builder"
},
{
"name": "create sampler builder",
"returns": "sampler builder"
@ -403,6 +424,38 @@
{"value": 1, "name":"linear"}
]
},
"framebuffer": {
"category": "object"
},
"framebuffer builder": {
"category": "object",
"methods": [
{
"name": "get result",
"returns": "framebuffer"
},
{
"name": "set render pass",
"args": [
{"name": "render pass", "type": "render pass"}
]
},
{
"name": "set dimensions",
"args": [
{"name": "width", "type": "uint32_t"},
{"name": "height", "type": "uint32_t"}
]
},
{
"name": "set attachment",
"args": [
{"name": "attachment slot", "type": "uint32_t"},
{"name": "texture views", "type": "texture view"}
]
}
]
},
"index format": {
"category": "enum",
"values": [
@ -462,6 +515,13 @@
{"name": "layout", "type": "pipeline layout"}
]
},
{
"name": "set subpass",
"args": [
{"name": "render pass", "type": "render pass"},
{"name": "subpass", "type": "uint32_t"}
]
},
{
"name": "set stage",
"args": [
@ -518,6 +578,50 @@
}
]
},
"render pass builder": {
"category": "object",
"TODO": {
"attachments": "Also need load op, store op, depth/stencil attachments and depth/stencil ops, and maybe usages for the implicit attachment transitions",
"subpasses": "Also need input attachments, resolve attachments, and preserve attachments"
},
"methods": [
{
"name": "get result",
"returns": "render pass"
},
{
"name": "set attachment count",
"args": [
{"name": "attachment count", "type": "uint32_t"}
]
},
{
"name": "attachment set format",
"TODO": "Also need sample count",
"args": [
{"name": "attachment slot", "type": "uint32_t"},
{"name": "format", "type": "texture format"}
]
},
{
"name": "set subpass count",
"args": [
{"name": "subpass count", "type": "uint32_t"}
]
},
{
"name": "subpass set color attachment",
"args": [
{"name": "subpass index", "type": "uint32_t"},
{"name": "output attachment location", "type": "uint32_t"},
{"name": "attachment slot", "type": "uint32_t"}
]
}
]
},
"render pass": {
"category": "object"
},
"sampler": {
"category": "object"
},

View File

@ -34,6 +34,8 @@ list(APPEND BACKEND_SOURCES
${COMMON_DIR}/Device.cpp
${COMMON_DIR}/Device.h
${COMMON_DIR}/Forward.h
${COMMON_DIR}/Framebuffer.cpp
${COMMON_DIR}/Framebuffer.h
${COMMON_DIR}/InputState.cpp
${COMMON_DIR}/InputState.h
${COMMON_DIR}/Math.cpp
@ -46,6 +48,8 @@ list(APPEND BACKEND_SOURCES
${COMMON_DIR}/PipelineLayout.h
${COMMON_DIR}/Queue.cpp
${COMMON_DIR}/Queue.h
${COMMON_DIR}/RenderPass.cpp
${COMMON_DIR}/RenderPass.h
${COMMON_DIR}/RefCounted.cpp
${COMMON_DIR}/RefCounted.h
${COMMON_DIR}/Sampler.cpp

View File

@ -114,12 +114,14 @@ namespace backend {
HandleError("Setting bindings type over maximum number of bindings");
return;
}
for (size_t i = start; i < start + count; i++) {
if (bindingInfo.mask[i]) {
HandleError("Setting already set binding type");
return;
}
}
for (size_t i = start; i < start + count; i++) {
bindingInfo.mask.set(i);
bindingInfo.visibilities[i] = visibility;
bindingInfo.types[i] = bindingType;

View File

@ -55,6 +55,18 @@ namespace backend {
Command type;
while(commands->NextCommandId(&type)) {
switch (type) {
case Command::AdvanceSubpass:
{
AdvanceSubpassCmd* cmd = commands->NextCommand<AdvanceSubpassCmd>();
cmd->~AdvanceSubpassCmd();
}
break;
case Command::BeginRenderPass:
{
BeginRenderPassCmd* begin = commands->NextCommand<BeginRenderPassCmd>();
begin->~BeginRenderPassCmd();
}
break;
case Command::CopyBufferToTexture:
{
CopyBufferToTextureCmd* copy = commands->NextCommand<CopyBufferToTextureCmd>();
@ -79,6 +91,12 @@ namespace backend {
draw->~DrawElementsCmd();
}
break;
case Command::EndRenderPass:
{
EndRenderPassCmd* cmd = commands->NextCommand<EndRenderPassCmd>();
cmd->~EndRenderPassCmd();
}
break;
case Command::SetPipeline:
{
SetPipelineCmd* cmd = commands->NextCommand<SetPipelineCmd>();
@ -148,6 +166,7 @@ namespace backend {
VALIDATION_ASPECT_BINDGROUPS,
VALIDATION_ASPECT_VERTEX_BUFFERS,
VALIDATION_ASPECT_INDEX_BUFFER,
VALIDATION_ASPECT_RENDER_PASS,
VALIDATION_ASPECT_COUNT,
};
@ -162,6 +181,7 @@ namespace backend {
std::bitset<kMaxVertexInputs> inputsSet;
PipelineBase* lastPipeline = nullptr;
// TODO(kainino@chromium.org): Manage this state inside an object, change lambdas to methods
std::map<BufferBase*, nxt::BufferUsageBit> mostRecentBufferUsages;
auto bufferHasGuaranteedUsageBit = [&](BufferBase* buffer, nxt::BufferUsageBit usage) -> bool {
assert(usage != nxt::BufferUsageBit::None && nxt::HasZeroOrOneBits(usage));
@ -234,9 +254,84 @@ namespace backend {
return true;
};
// TODO(kainino@chromium.org): Manage this state inside an object, change lambda to a method
RenderPassBase* currentRenderPass = nullptr;
FramebufferBase* currentFramebuffer = nullptr;
uint32_t currentSubpass = 0;
auto beginSubpass = [&]() -> bool {
auto& subpassInfo = currentRenderPass->GetSubpassInfo(currentSubpass);
for (auto attachmentSlot : subpassInfo.colorAttachments) {
auto* tv = currentFramebuffer->GetTextureView(attachmentSlot);
// TODO(kainino@chromium.org): the TextureView can only be null
// because of the null=backbuffer hack (null representing the
// backbuffer). Once that hack is removed (once we have WSI)
// this check isn't needed.
if (tv == nullptr) {
continue;
}
auto* texture = tv->GetTexture();
if (texture->HasFrozenUsage(nxt::TextureUsageBit::ColorAttachment)) {
continue;
}
if (!texture->IsTransitionPossible(nxt::TextureUsageBit::ColorAttachment)) {
HandleError("Can't transition attachment to ColorAttachment usage");
return false;
}
mostRecentTextureUsages.erase(texture);
texturesTransitioned.insert(texture);
}
return true;
};
Command type;
while(iterator.NextCommandId(&type)) {
switch (type) {
case Command::AdvanceSubpass:
{
iterator.NextCommand<AdvanceSubpassCmd>();
if (currentRenderPass == nullptr) {
HandleError("Can't advance subpass without an active render pass");
return false;
}
if (currentSubpass + 1 >= currentRenderPass->GetSubpassCount()) {
HandleError("Can't advance beyond the last subpass");
return false;
}
currentSubpass += 1;
if (!beginSubpass()) {
return false;
}
aspects.reset(VALIDATION_ASPECT_RENDER_PIPELINE);
}
break;
case Command::BeginRenderPass:
{
BeginRenderPassCmd* begin = iterator.NextCommand<BeginRenderPassCmd>();
auto* renderPass = begin->renderPass.Get();
auto* framebuffer = begin->framebuffer.Get();
if (currentRenderPass != nullptr) {
HandleError("A render pass is already active");
return false;
}
if (!framebuffer->GetRenderPass()->IsCompatibleWith(renderPass)) {
HandleError("Framebuffer is incompatible with this render pass");
return false;
}
aspects.reset(VALIDATION_ASPECT_COMPUTE_PIPELINE);
aspects.reset(VALIDATION_ASPECT_RENDER_PIPELINE);
aspects.set(VALIDATION_ASPECT_RENDER_PASS);
currentRenderPass = renderPass;
currentFramebuffer = framebuffer;
currentSubpass = 0;
if (!beginSubpass()) {
return false;
}
}
break;
case Command::CopyBufferToTexture:
{
CopyBufferToTextureCmd* copy = iterator.NextCommand<CopyBufferToTextureCmd>();
@ -251,6 +346,11 @@ namespace backend {
uint64_t z = copy->z;
uint32_t level = copy->level;
if (currentRenderPass) {
HandleError("Blit cannot occur during a render pass");
return false;
}
if (!bufferHasGuaranteedUsageBit(buffer, nxt::BufferUsageBit::TransferSrc)) {
HandleError("Buffer needs the transfer source usage bit");
return false;
@ -354,6 +454,24 @@ namespace backend {
}
break;
case Command::EndRenderPass:
{
iterator.NextCommand<EndRenderPassCmd>();
if (currentRenderPass == nullptr) {
HandleError("No render pass is currently active");
return false;
}
if (currentSubpass < currentRenderPass->GetSubpassCount() - 1) {
HandleError("Can't end a render pass before the last subpass");
return false;
}
currentRenderPass = nullptr;
currentFramebuffer = nullptr;
aspects.reset(VALIDATION_ASPECT_RENDER_PASS);
aspects.reset(VALIDATION_ASPECT_RENDER_PIPELINE);
}
break;
case Command::SetPipeline:
{
SetPipelineCmd* cmd = iterator.NextCommand<SetPipelineCmd>();
@ -361,11 +479,21 @@ namespace backend {
PipelineLayoutBase* layout = pipeline->GetLayout();
if (pipeline->IsCompute()) {
if (currentRenderPass) {
HandleError("Can't use a compute pipeline while a render pass is active");
return false;
}
aspects.set(VALIDATION_ASPECT_COMPUTE_PIPELINE);
aspects.reset(VALIDATION_ASPECT_RENDER_PIPELINE);
} else {
if (!currentRenderPass) {
HandleError("A render pass must be active when a render pipeline is set");
return false;
}
if (!pipeline->GetRenderPass()->IsCompatibleWith(currentRenderPass)) {
HandleError("Pipeline is incompatible with this render pass");
return false;
}
aspects.set(VALIDATION_ASPECT_RENDER_PIPELINE);
aspects.reset(VALIDATION_ASPECT_COMPUTE_PIPELINE);
}
aspects.reset(VALIDATION_ASPECT_BINDGROUPS);
aspects.reset(VALIDATION_ASPECT_VERTEX_BUFFERS);
@ -472,6 +600,29 @@ namespace backend {
return false;
}
// TODO(kainino@chromium.org): We should be able to
// optimize this by tracking which textures are in use
// as attachments. Maybe it's possible that the
// XAttachment usages could mark this: disallow
// explicit transitions to XAttachment usages, and
// disallow explicit transitions of resources already
// in an XAttachment usage.
if (currentRenderPass) {
auto& info = currentRenderPass->GetSubpassInfo(currentSubpass);
for (uint32_t location = 0; location < info.colorAttachments.size(); ++location) {
if (!info.colorAttachmentsSet[location]) {
continue;
}
uint32_t attachmentSlot = info.colorAttachments[location];
auto* tv = currentFramebuffer->GetTextureView(attachmentSlot);
// TODO(kainino@chromium.org): remove check for tv being non-null once the null=backbuffer hack is removed.
if (tv && tv->GetTexture() == texture) {
HandleError("Can't transition a texture while it's used as a color attachment");
return false;
}
}
}
mostRecentTextureUsages[texture] = usage;
texturesTransitioned.insert(texture);
@ -494,6 +645,17 @@ namespace backend {
return device->CreateCommandBuffer(this);
}
void CommandBufferBuilder::AdvanceSubpass() {
allocator.Allocate<AdvanceSubpassCmd>(Command::AdvanceSubpass);
}
void CommandBufferBuilder::BeginRenderPass(RenderPassBase* renderPass, FramebufferBase* framebuffer) {
BeginRenderPassCmd* cmd = allocator.Allocate<BeginRenderPassCmd>(Command::BeginRenderPass);
new(cmd) BeginRenderPassCmd;
cmd->renderPass = renderPass;
cmd->framebuffer = framebuffer;
}
void CommandBufferBuilder::CopyBufferToTexture(BufferBase* buffer, uint32_t bufferOffset,
TextureBase* texture, uint32_t x, uint32_t y, uint32_t z,
uint32_t width, uint32_t height, uint32_t depth, uint32_t level) {
@ -537,6 +699,10 @@ namespace backend {
draw->firstInstance = firstInstance;
}
void CommandBufferBuilder::EndRenderPass() {
allocator.Allocate<EndRenderPassCmd>(Command::EndRenderPass);
}
void CommandBufferBuilder::SetPipeline(PipelineBase* pipeline) {
SetPipelineCmd* cmd = allocator.Allocate<SetPipelineCmd>(Command::SetPipeline);
new(cmd) SetPipelineCmd;

View File

@ -28,8 +28,10 @@ namespace backend {
class BindGroupBase;
class BufferBase;
class FramebufferBase;
class DeviceBase;
class PipelineBase;
class RenderPassBase;
class TextureBase;
class CommandBufferBuilder;
@ -55,12 +57,15 @@ namespace backend {
CommandIterator AcquireCommands();
// NXT API
void AdvanceSubpass();
void BeginRenderPass(RenderPassBase* renderPass, FramebufferBase* framebuffer);
void CopyBufferToTexture(BufferBase* buffer, uint32_t bufferOffset,
TextureBase* texture, uint32_t x, uint32_t y, uint32_t z,
uint32_t width, uint32_t height, uint32_t depth, uint32_t level);
void Dispatch(uint32_t x, uint32_t y, uint32_t z);
void DrawArrays(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance);
void DrawElements(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstIndex, uint32_t firstInstance);
void EndRenderPass();
void SetPushConstants(nxt::ShaderStageBit stage, uint32_t offset, uint32_t count, const void* data);
void SetPipeline(PipelineBase* pipeline);
void SetBindGroup(uint32_t groupIndex, BindGroupBase* group);

View File

@ -15,6 +15,8 @@
#ifndef BACKEND_COMMON_COMMANDS_H_
#define BACKEND_COMMON_COMMANDS_H_
#include "Framebuffer.h"
#include "RenderPass.h"
#include "Texture.h"
#include "nxt/nxtcpp.h"
@ -26,10 +28,13 @@ namespace backend {
// dependencies: Ref<Object> needs Object to be defined.
enum class Command {
AdvanceSubpass,
BeginRenderPass,
CopyBufferToTexture,
Dispatch,
DrawArrays,
DrawElements,
EndRenderPass,
SetPipeline,
SetPushConstants,
SetBindGroup,
@ -39,6 +44,14 @@ namespace backend {
TransitionTextureUsage,
};
struct AdvanceSubpassCmd {
};
struct BeginRenderPassCmd {
Ref<RenderPassBase> renderPass;
Ref<FramebufferBase> framebuffer;
};
struct CopyBufferToTextureCmd {
Ref<BufferBase> buffer;
uint32_t bufferOffset;
@ -68,6 +81,9 @@ namespace backend {
uint32_t firstInstance;
};
struct EndRenderPassCmd {
};
struct SetPipelineCmd {
Ref<PipelineBase> pipeline;
};

View File

@ -18,10 +18,12 @@
#include "BindGroupLayout.h"
#include "Buffer.h"
#include "CommandBuffer.h"
#include "Framebuffer.h"
#include "InputState.h"
#include "Pipeline.h"
#include "PipelineLayout.h"
#include "Queue.h"
#include "RenderPass.h"
#include "Sampler.h"
#include "ShaderModule.h"
#include "Texture.h"
@ -96,6 +98,9 @@ namespace backend {
CommandBufferBuilder* DeviceBase::CreateCommandBufferBuilder() {
return new CommandBufferBuilder(this);
}
FramebufferBuilder* DeviceBase::CreateFramebufferBuilder() {
return new FramebufferBuilder(this);
}
InputStateBuilder* DeviceBase::CreateInputStateBuilder() {
return new InputStateBuilder(this);
}
@ -108,6 +113,9 @@ namespace backend {
QueueBuilder* DeviceBase::CreateQueueBuilder() {
return new QueueBuilder(this);
}
RenderPassBuilder* DeviceBase::CreateRenderPassBuilder() {
return new RenderPassBuilder(this);
}
SamplerBuilder* DeviceBase::CreateSamplerBuilder() {
return new SamplerBuilder(this);
}

View File

@ -40,10 +40,12 @@ namespace backend {
virtual BufferBase* CreateBuffer(BufferBuilder* builder) = 0;
virtual BufferViewBase* CreateBufferView(BufferViewBuilder* builder) = 0;
virtual CommandBufferBase* CreateCommandBuffer(CommandBufferBuilder* builder) = 0;
virtual FramebufferBase* CreateFramebuffer(FramebufferBuilder* builder) = 0;
virtual InputStateBase* CreateInputState(InputStateBuilder* builder) = 0;
virtual PipelineBase* CreatePipeline(PipelineBuilder* builder) = 0;
virtual PipelineLayoutBase* CreatePipelineLayout(PipelineLayoutBuilder* builder) = 0;
virtual QueueBase* CreateQueue(QueueBuilder* builder) = 0;
virtual RenderPassBase* CreateRenderPass(RenderPassBuilder* builder) = 0;
virtual SamplerBase* CreateSampler(SamplerBuilder* builder) = 0;
virtual ShaderModuleBase* CreateShaderModule(ShaderModuleBuilder* builder) = 0;
virtual TextureBase* CreateTexture(TextureBuilder* builder) = 0;
@ -72,10 +74,12 @@ namespace backend {
BufferBuilder* CreateBufferBuilder();
BufferViewBuilder* CreateBufferViewBuilder();
CommandBufferBuilder* CreateCommandBufferBuilder();
FramebufferBuilder* CreateFramebufferBuilder();
InputStateBuilder* CreateInputStateBuilder();
PipelineBuilder* CreatePipelineBuilder();
PipelineLayoutBuilder* CreatePipelineLayoutBuilder();
QueueBuilder* CreateQueueBuilder();
RenderPassBuilder* CreateRenderPassBuilder();
SamplerBuilder* CreateSamplerBuilder();
ShaderModuleBuilder* CreateShaderModuleBuilder();
TextureBuilder* CreateTextureBuilder();

View File

@ -32,6 +32,8 @@ namespace backend {
class BufferViewBuilder;
class CommandBufferBase;
class CommandBufferBuilder;
class FramebufferBase;
class FramebufferBuilder;
class InputStateBase;
class InputStateBuilder;
class PipelineBase;
@ -40,6 +42,8 @@ namespace backend {
class PipelineLayoutBuilder;
class QueueBase;
class QueueBuilder;
class RenderPassBase;
class RenderPassBuilder;
class SamplerBase;
class SamplerBuilder;
class ShaderModuleBase;
@ -64,6 +68,7 @@ namespace backend {
static constexpr uint32_t kMaxVertexAttributes = 16u;
static constexpr uint32_t kMaxVertexInputs = 16u;
static constexpr uint32_t kNumStages = 3;
static constexpr uint32_t kMaxColorAttachments = 4u;
enum PushConstantType : uint8_t;
}

View File

@ -0,0 +1,120 @@
// Copyright 2017 The NXT Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "Framebuffer.h"
#include "Buffer.h"
#include "Device.h"
#include "RenderPass.h"
#include "Texture.h"
namespace backend {
// Framebuffer
FramebufferBase::FramebufferBase(FramebufferBuilder* builder)
: renderPass(std::move(builder->renderPass)), width(builder->width), height(builder->height), textureViews(std::move(builder->textureViews)) {
}
RenderPassBase* FramebufferBase::GetRenderPass() {
return renderPass.Get();
}
TextureViewBase* FramebufferBase::GetTextureView(uint32_t index) {
ASSERT(index < textureViews.size());
return textureViews[index].Get();
}
// FramebufferBuilder
enum FramebufferSetProperties {
FRAMEBUFFER_PROPERTY_RENDER_PASS = 0x1,
FRAMEBUFFER_PROPERTY_DIMENSIONS = 0x2,
};
FramebufferBuilder::FramebufferBuilder(DeviceBase* device)
: Builder(device) {
}
FramebufferBase* FramebufferBuilder::GetResultImpl() {
constexpr int requiredProperties = FRAMEBUFFER_PROPERTY_RENDER_PASS | FRAMEBUFFER_PROPERTY_DIMENSIONS;
if ((propertiesSet & requiredProperties) != requiredProperties) {
HandleError("Framebuffer missing properties");
return nullptr;
}
// TODO(kainino@chromium.org): Remove this flag when the
// null=backbuffer hack is removed. Then, using null texture views can
// be completely disallowed.
bool usingBackbufferHack = false;
for (auto& textureView : textureViews) {
if (!textureView) {
if (usingBackbufferHack) {
// TODO(kainino@chromium.org) update this too
HandleError("Framebuffer has more than one null attachment");
return nullptr;
}
usingBackbufferHack = true;
}
}
return device->CreateFramebuffer(this);
}
void FramebufferBuilder::SetRenderPass(RenderPassBase* renderPass) {
if ((propertiesSet & FRAMEBUFFER_PROPERTY_RENDER_PASS) != 0) {
HandleError("Framebuffer render pass property set multiple times");
return;
}
this->renderPass = renderPass;
this->textureViews.resize(renderPass->GetAttachmentCount());
propertiesSet |= FRAMEBUFFER_PROPERTY_RENDER_PASS;
}
void FramebufferBuilder::SetDimensions(uint32_t width, uint32_t height) {
if ((propertiesSet & FRAMEBUFFER_PROPERTY_DIMENSIONS) != 0) {
HandleError("Framebuffer dimensions property set multiple times");
return;
}
this->width = width;
this->height = height;
propertiesSet |= FRAMEBUFFER_PROPERTY_DIMENSIONS;
}
void FramebufferBuilder::SetAttachment(uint32_t attachmentSlot, TextureViewBase* textureView) {
if ((propertiesSet & FRAMEBUFFER_PROPERTY_RENDER_PASS) == 0) {
HandleError("Render pass must be set before framebuffer attachments");
return;
}
if (attachmentSlot >= textureViews.size()) {
HandleError("Attachment slot out of bounds");
return;
}
if (textureViews[attachmentSlot]) {
HandleError("Framebuffer attachment[i] set multiple times");
return;
}
const auto& attachmentInfo = renderPass->GetAttachmentInfo(attachmentSlot);
const auto* texture = textureView->GetTexture();
if (attachmentInfo.format != texture->GetFormat()) {
HandleError("Texture format does not match attachment format");
return;
}
// TODO(kainino@chromium.org): also check attachment samples, etc.
textureViews[attachmentSlot] = textureView;
}
}

View File

@ -0,0 +1,67 @@
// Copyright 2017 The NXT Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef BACKEND_COMMON_FRAMEBUFFER_H_
#define BACKEND_COMMON_FRAMEBUFFER_H_
#include "Builder.h"
#include "Forward.h"
#include "RefCounted.h"
#include "nxt/nxtcpp.h"
#include <type_traits>
#include <vector>
namespace backend {
class FramebufferBase : public RefCounted {
public:
FramebufferBase(FramebufferBuilder* builder);
RenderPassBase* GetRenderPass();
TextureViewBase* GetTextureView(uint32_t index);
private:
Ref<RenderPassBase> renderPass;
uint32_t width = 0;
uint32_t height = 0;
std::vector<Ref<TextureViewBase>> textureViews;
};
class FramebufferBuilder : public Builder<FramebufferBase> {
public:
FramebufferBuilder(DeviceBase* device);
bool WasConsumed() const;
// NXT API
FramebufferBase* GetResultImpl() override;
void SetRenderPass(RenderPassBase* renderPass);
void SetDimensions(uint32_t width, uint32_t height);
void SetAttachment(uint32_t attachmentSlot, TextureViewBase* textureView);
private:
friend class FramebufferBase;
Ref<RenderPassBase> renderPass;
uint32_t width = 0;
uint32_t height = 0;
std::vector<Ref<TextureViewBase>> textureViews;
int propertiesSet = 0;
};
}
#endif // BACKEND_COMMON_FRAMEBUFFER_H_

View File

@ -17,6 +17,7 @@
#include "Device.h"
#include "InputState.h"
#include "PipelineLayout.h"
#include "RenderPass.h"
#include "ShaderModule.h"
namespace backend {
@ -25,6 +26,7 @@ namespace backend {
PipelineBase::PipelineBase(PipelineBuilder* builder)
: device(builder->device), stageMask(builder->stageMask), layout(std::move(builder->layout)),
renderPass(std::move(builder->renderPass)), subpass(builder->subpass),
inputState(std::move(builder->inputState)) {
if (stageMask != (nxt::ShaderStageBit::Vertex | nxt::ShaderStageBit::Fragment) &&
@ -33,6 +35,12 @@ namespace backend {
return;
}
if (!IsCompute() && !renderPass) {
builder->HandleError("Pipeline render pass not set");
return;
}
// TODO(kainino@chromium.org): Need to verify the pipeline against its render subpass.
auto FillPushConstants = [](const ShaderModuleBase* module, PushConstantInfo* info) {
const auto& moduleInfo = module->GetPushConstants();
info->mask = moduleInfo.mask;
@ -79,6 +87,10 @@ namespace backend {
return layout.Get();
}
RenderPassBase* PipelineBase::GetRenderPass() {
return renderPass.Get();
}
InputStateBase* PipelineBase::GetInputState() {
return inputState.Get();
}
@ -114,6 +126,11 @@ namespace backend {
this->layout = layout;
}
void PipelineBuilder::SetSubpass(RenderPassBase* renderPass, uint32_t subpass) {
this->renderPass = renderPass;
this->subpass = subpass;
}
void PipelineBuilder::SetStage(nxt::ShaderStage stage, ShaderModuleBase* module, const char* entryPoint) {
if (entryPoint != std::string("main")) {
HandleError("Currently the entry point has to be main()");

View File

@ -45,6 +45,7 @@ namespace backend {
nxt::ShaderStageBit GetStageMask() const;
PipelineLayoutBase* GetLayout();
RenderPassBase* GetRenderPass();
InputStateBase* GetInputState();
// TODO(cwallez@chromium.org): split compute and render pipelines
@ -55,6 +56,8 @@ namespace backend {
nxt::ShaderStageBit stageMask;
Ref<PipelineLayoutBase> layout;
Ref<RenderPassBase> renderPass;
uint32_t subpass;
PerStage<PushConstantInfo> pushConstants;
Ref<InputStateBase> inputState;
};
@ -71,6 +74,7 @@ namespace backend {
// NXT API
void SetLayout(PipelineLayoutBase* layout);
void SetSubpass(RenderPassBase* renderPass, uint32_t subpass);
void SetStage(nxt::ShaderStage stage, ShaderModuleBase* module, const char* entryPoint);
void SetInputState(InputStateBase* inputState);
@ -80,6 +84,8 @@ namespace backend {
PipelineBase* GetResultImpl() override;
Ref<PipelineLayoutBase> layout;
Ref<RenderPassBase> renderPass;
uint32_t subpass;
nxt::ShaderStageBit stageMask;
PerStage<StageInfo> stages;
Ref<InputStateBase> inputState;

View File

@ -0,0 +1,149 @@
// Copyright 2017 The NXT Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "RenderPass.h"
#include "Buffer.h"
#include "Device.h"
#include "Texture.h"
namespace backend {
// RenderPass
RenderPassBase::RenderPassBase(RenderPassBuilder* builder)
: attachments(std::move(builder->attachments)), subpasses(std::move(builder->subpasses)) {
}
uint32_t RenderPassBase::GetAttachmentCount() const {
return attachments.size();
}
const RenderPassBase::AttachmentInfo& RenderPassBase::GetAttachmentInfo(uint32_t attachment) const {
ASSERT(attachment < attachments.size());
return attachments[attachment];
}
uint32_t RenderPassBase::GetSubpassCount() const {
return subpasses.size();
}
const RenderPassBase::SubpassInfo& RenderPassBase::GetSubpassInfo(uint32_t subpass) const {
ASSERT(subpass < subpasses.size());
return subpasses[subpass];
}
bool RenderPassBase::IsCompatibleWith(const RenderPassBase* other) const {
// TODO(kainino@chromium.org): This check is overly strict; need actual
// compatibility checking (different load and store ops, etc.)
return other == this;
}
// RenderPassBuilder
enum RenderPassSetProperties {
RENDERPASS_PROPERTY_ATTACHMENT_COUNT = 0x1,
RENDERPASS_PROPERTY_SUBPASS_COUNT = 0x2,
};
RenderPassBuilder::RenderPassBuilder(DeviceBase* device)
: Builder(device), subpasses(1) {
}
RenderPassBase* RenderPassBuilder::GetResultImpl() {
constexpr int requiredProperties = RENDERPASS_PROPERTY_ATTACHMENT_COUNT | RENDERPASS_PROPERTY_SUBPASS_COUNT;
if ((propertiesSet & requiredProperties) != requiredProperties) {
HandleError("Render pass missing properties");
return nullptr;
}
for (const auto& prop : attachmentProperties) {
if (!prop.all()) {
HandleError("A render pass attachment is missing some property");
return nullptr;
}
}
return device->CreateRenderPass(this);
}
void RenderPassBuilder::SetAttachmentCount(uint32_t attachmentCount) {
if ((propertiesSet & RENDERPASS_PROPERTY_ATTACHMENT_COUNT) != 0) {
HandleError("Render pass attachment count property set multiple times");
return;
}
attachmentProperties.resize(attachmentCount);
attachments.resize(attachmentCount);
propertiesSet |= RENDERPASS_PROPERTY_ATTACHMENT_COUNT;
}
void RenderPassBuilder::AttachmentSetFormat(uint32_t attachmentSlot, nxt::TextureFormat format) {
if ((propertiesSet & RENDERPASS_PROPERTY_ATTACHMENT_COUNT) == 0) {
HandleError("Render pass attachment count not set yet");
return;
}
if (attachmentSlot > attachments.size()) {
HandleError("Render pass attachment index out of bounds");
return;
}
if (attachmentProperties[attachmentSlot][ATTACHMENT_PROPERTY_FORMAT]) {
HandleError("Render pass attachment format already set");
return;
}
attachments[attachmentSlot].format = format;
attachmentProperties[attachmentSlot].set(ATTACHMENT_PROPERTY_FORMAT);
}
void RenderPassBuilder::SetSubpassCount(uint32_t subpassCount) {
if ((propertiesSet & RENDERPASS_PROPERTY_SUBPASS_COUNT) != 0) {
HandleError("Render pass subpass count property set multiple times");
return;
}
if (subpassCount < 1) {
HandleError("Render pass cannot have fewer than one subpass");
return;
}
subpasses.resize(subpassCount);
propertiesSet |= RENDERPASS_PROPERTY_SUBPASS_COUNT;
}
void RenderPassBuilder::SubpassSetColorAttachment(uint32_t subpass, uint32_t outputAttachmentLocation, uint32_t attachmentSlot) {
if ((propertiesSet & RENDERPASS_PROPERTY_SUBPASS_COUNT) == 0) {
HandleError("Render pass subpass count not set yet");
return;
}
if (subpass >= subpasses.size()) {
HandleError("Subpass index out of bounds");
return;
}
if (outputAttachmentLocation >= kMaxColorAttachments) {
HandleError("Subpass output attachment location out of bounds");
return;
}
if (attachmentSlot >= attachments.size()) {
HandleError("Subpass attachment slot out of bounds");
return;
}
if (subpasses[subpass].colorAttachmentsSet[outputAttachmentLocation]) {
HandleError("Subpass color attachment already set");
return;
}
subpasses[subpass].colorAttachmentsSet.set(outputAttachmentLocation);
subpasses[subpass].colorAttachments[outputAttachmentLocation] = attachmentSlot;
}
}

View File

@ -0,0 +1,83 @@
// Copyright 2017 The NXT Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef BACKEND_COMMON_RENDERPASS_H_
#define BACKEND_COMMON_RENDERPASS_H_
#include "Builder.h"
#include "Forward.h"
#include "RefCounted.h"
#include "nxt/nxtcpp.h"
#include <array>
#include <bitset>
#include <vector>
namespace backend {
class RenderPassBase : public RefCounted {
public:
RenderPassBase(RenderPassBuilder* builder);
struct AttachmentInfo {
nxt::TextureFormat format;
};
struct SubpassInfo {
std::bitset<kMaxColorAttachments> colorAttachmentsSet;
std::array<uint32_t, kMaxColorAttachments> colorAttachments;
};
uint32_t GetAttachmentCount() const;
const AttachmentInfo& GetAttachmentInfo(uint32_t attachment) const;
uint32_t GetSubpassCount() const;
const SubpassInfo& GetSubpassInfo(uint32_t subpass) const;
bool IsCompatibleWith(const RenderPassBase* other) const;
private:
std::vector<AttachmentInfo> attachments;
std::vector<SubpassInfo> subpasses;
};
class RenderPassBuilder : public Builder<RenderPassBase> {
public:
RenderPassBuilder(DeviceBase* device);
bool WasConsumed() const;
// NXT API
RenderPassBase* GetResultImpl() override;
void SetAttachmentCount(uint32_t attachmentCount);
void AttachmentSetFormat(uint32_t attachmentSlot, nxt::TextureFormat format);
void SetSubpassCount(uint32_t subpassCount);
void SubpassSetColorAttachment(uint32_t subpass, uint32_t outputAttachmentLocation, uint32_t attachmentSlot);
private:
friend class RenderPassBase;
enum AttachmentProperty {
ATTACHMENT_PROPERTY_FORMAT,
ATTACHMENT_PROPERTY_COUNT
};
std::vector<std::bitset<ATTACHMENT_PROPERTY_COUNT>> attachmentProperties;
std::vector<RenderPassBase::AttachmentInfo> attachments;
std::vector<RenderPassBase::SubpassInfo> subpasses;
int propertiesSet = 0;
};
}
#endif // BACKEND_COMMON_RENDERPASS_H_

View File

@ -48,6 +48,11 @@ namespace backend {
using BackendType = typename BackendTraits::CommandBufferType;
};
template<typename BackendTraits>
struct ToBackendTraits<FramebufferBase, BackendTraits> {
using BackendType = typename BackendTraits::FramebufferType;
};
template<typename BackendTraits>
struct ToBackendTraits<InputStateBase, BackendTraits> {
using BackendType = typename BackendTraits::InputStateType;
@ -68,6 +73,11 @@ namespace backend {
using BackendType = typename BackendTraits::QueueType;
};
template<typename BackendTraits>
struct ToBackendTraits<RenderPassBase, BackendTraits> {
using BackendType = typename BackendTraits::RenderPassType;
};
template<typename BackendTraits>
struct ToBackendTraits<SamplerBase, BackendTraits> {
using BackendType = typename BackendTraits::SamplerType;

View File

@ -27,9 +27,11 @@
#include "common/Device.h"
#include "common/CommandBuffer.h"
#include "common/InputState.h"
#include "common/Framebuffer.h"
#include "common/Pipeline.h"
#include "common/PipelineLayout.h"
#include "common/Queue.h"
#include "common/RenderPass.h"
#include "common/Sampler.h"
#include "common/ShaderModule.h"
#include "common/Texture.h"
@ -52,9 +54,11 @@ namespace metal {
class BufferView;
class CommandBuffer;
class InputState;
class Framebuffer;
class Pipeline;
class PipelineLayout;
class Queue;
class RenderPass;
class Sampler;
class ShaderModule;
class Texture;
@ -67,9 +71,11 @@ namespace metal {
using BufferViewType = BufferView;
using CommandBufferType = CommandBuffer;
using InputStateType = InputState;
using FramebufferType = Framebuffer;
using PipelineType = Pipeline;
using PipelineLayoutType = PipelineLayout;
using QueueType = Queue;
using RenderPassType = RenderPass;
using SamplerType = Sampler;
using ShaderModuleType = ShaderModule;
using TextureType = Texture;
@ -92,9 +98,11 @@ namespace metal {
BufferViewBase* CreateBufferView(BufferViewBuilder* builder) override;
CommandBufferBase* CreateCommandBuffer(CommandBufferBuilder* builder) override;
InputStateBase* CreateInputState(InputStateBuilder* builder) override;
FramebufferBase* CreateFramebuffer(FramebufferBuilder* builder) override;
PipelineBase* CreatePipeline(PipelineBuilder* builder) override;
PipelineLayoutBase* CreatePipelineLayout(PipelineLayoutBuilder* builder) override;
QueueBase* CreateQueue(QueueBuilder* builder) override;
RenderPassBase* CreateRenderPass(RenderPassBuilder* builder) override;
SamplerBase* CreateSampler(SamplerBuilder* builder) override;
ShaderModuleBase* CreateShaderModule(ShaderModuleBuilder* builder) override;
TextureBase* CreateTexture(TextureBuilder* builder) override;
@ -184,6 +192,15 @@ namespace metal {
MTLVertexDescriptor* mtlVertexDescriptor = nil;
};
class Framebuffer : public FramebufferBase {
public:
Framebuffer(Device* device, FramebufferBuilder* builder);
~Framebuffer();
private:
Device* device;
};
class Pipeline : public PipelineBase {
public:
Pipeline(Device* device, PipelineBuilder* builder);
@ -230,6 +247,15 @@ namespace metal {
id<MTLCommandQueue> commandQueue = nil;
};
class RenderPass : public RenderPassBase {
public:
RenderPass(Device* device, RenderPassBuilder* builder);
~RenderPass();
private:
Device* device;
};
class Sampler : public SamplerBase {
public:
Sampler(Device* device, SamplerBuilder* builder);

View File

@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// TODO(kainino@chromium.org): split this backend into many files
#include "MetalBackend.h"
#include <spirv-cross/spirv_msl.hpp>
@ -81,6 +83,9 @@ namespace metal {
InputStateBase* Device::CreateInputState(InputStateBuilder* builder) {
return new InputState(this, builder);
}
FramebufferBase* Device::CreateFramebuffer(FramebufferBuilder* builder) {
return new Framebuffer(this, builder);
}
PipelineBase* Device::CreatePipeline(PipelineBuilder* builder) {
return new Pipeline(this, builder);
}
@ -90,6 +95,9 @@ namespace metal {
QueueBase* Device::CreateQueue(QueueBuilder* builder) {
return new Queue(this, builder);
}
RenderPassBase* Device::CreateRenderPass(RenderPassBuilder* builder) {
return new RenderPass(this, builder);
}
SamplerBase* Device::CreateSampler(SamplerBuilder* builder) {
return new Sampler(this, builder);
}
@ -248,7 +256,10 @@ namespace metal {
id<MTLComputeCommandEncoder> compute = nil;
id<MTLRenderCommandEncoder> render = nil;
BeginRenderPassCmd* currentRenderPass = nullptr;
void FinishEncoders() {
ASSERT(render == nil);
if (blit != nil) {
[blit endEncoding];
blit = nil;
@ -257,10 +268,6 @@ namespace metal {
[compute endEncoding];
compute = nil;
}
if (render != nil) {
[render endEncoding];
render = nil;
}
}
void EnsureBlit(id<MTLCommandBuffer> commandBuffer) {
@ -276,22 +283,49 @@ namespace metal {
// TODO(cwallez@chromium.org): does any state need to be reset?
}
}
void EnsureRender(id<MTLCommandBuffer> commandBuffer) {
if (render == nil) {
FinishEncoders();
void BeginSubpass(id<MTLCommandBuffer> commandBuffer, uint32_t subpass) {
ASSERT(currentRenderPass);
if (render != nil) {
[render endEncoding];
render = nil;
}
const auto& info = currentRenderPass->renderPass->GetSubpassInfo(subpass);
auto& framebuffer = currentRenderPass->framebuffer;
// TODO(cwallez@chromium.org): this should be created from a renderpass subpass
MTLRenderPassDescriptor* descriptor = [MTLRenderPassDescriptor renderPassDescriptor];
descriptor.colorAttachments[0].texture = device->GetCurrentTexture();
descriptor.colorAttachments[0].loadAction = MTLLoadActionLoad;
descriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
bool usingBackbuffer = false; // HACK(kainino@chromium.org): workaround for not having depth attachments
for (uint32_t index = 0; index < info.colorAttachments.size(); ++index) {
uint32_t attachment = info.colorAttachments[index];
// TODO(kainino@chromium.org): currently a 'null' texture view
// falls back to the 'back buffer' but this should go away
// when we have WSI.
id<MTLTexture> texture = nil;
if (auto textureView = framebuffer->GetTextureView(attachment)) {
texture = ToBackend(textureView->GetTexture())->GetMTLTexture();
} else {
texture = device->GetCurrentTexture();
usingBackbuffer = true;
}
descriptor.colorAttachments[index].texture = texture;
descriptor.colorAttachments[index].loadAction = MTLLoadActionLoad;
descriptor.colorAttachments[index].storeAction = MTLStoreActionStore;
}
// TODO(kainino@chromium.org): load depth attachment from subpass
if (usingBackbuffer) {
descriptor.depthAttachment.texture = device->GetCurrentDepthTexture();
descriptor.depthAttachment.loadAction = MTLLoadActionLoad;
descriptor.depthAttachment.storeAction = MTLStoreActionStore;
}
render = [commandBuffer renderCommandEncoderWithDescriptor:descriptor];
// TODO(cwallez@chromium.org): does any state need to be reset?
}
void EndRenderPass() {
ASSERT(render != nil);
[render endEncoding];
render = nil;
}
};
@ -307,15 +341,34 @@ namespace metal {
CurrentEncoders encoders;
encoders.device = device;
uint32_t currentSubpass = 0;
id<MTLRenderCommandEncoder> renderEncoder = nil;
while (commands.NextCommandId(&type)) {
switch (type) {
case Command::AdvanceSubpass:
{
commands.NextCommand<AdvanceSubpassCmd>();
currentSubpass += 1;
encoders.BeginSubpass(commandBuffer, currentSubpass);
}
break;
case Command::BeginRenderPass:
{
encoders.currentRenderPass = commands.NextCommand<BeginRenderPassCmd>();
encoders.FinishEncoders();
currentSubpass = 0;
encoders.BeginSubpass(commandBuffer, currentSubpass);
}
break;
case Command::CopyBufferToTexture:
{
CopyBufferToTextureCmd* copy = commands.NextCommand<CopyBufferToTextureCmd>();
Buffer* buffer = ToBackend(copy->buffer.Get());
Texture* texture = ToBackend(copy->texture.Get());
// TODO(kainino@chromium.org): this has to be in a Blit encoder, not a Render encoder, so ordering is lost here
unsigned rowSize = copy->width * TextureFormatPixelSize(texture->GetFormat());
MTLOrigin origin;
origin.x = copy->x;
@ -356,7 +409,7 @@ namespace metal {
{
DrawArraysCmd* draw = commands.NextCommand<DrawArraysCmd>();
encoders.EnsureRender(commandBuffer);
ASSERT(encoders.render);
[encoders.render
drawPrimitives:MTLPrimitiveTypeTriangle
vertexStart:draw->firstVertex
@ -370,7 +423,7 @@ namespace metal {
{
DrawElementsCmd* draw = commands.NextCommand<DrawElementsCmd>();
encoders.EnsureRender(commandBuffer);
ASSERT(encoders.render);
[encoders.render
drawIndexedPrimitives:MTLPrimitiveTypeTriangle
indexCount:draw->indexCount
@ -383,6 +436,13 @@ namespace metal {
}
break;
case Command::EndRenderPass:
{
commands.NextCommand<EndRenderPassCmd>();
encoders.EndRenderPass();
}
break;
case Command::SetPipeline:
{
SetPipelineCmd* cmd = commands.NextCommand<SetPipelineCmd>();
@ -392,7 +452,7 @@ namespace metal {
encoders.EnsureCompute(commandBuffer);
lastPipeline->Encode(encoders.compute);
} else {
encoders.EnsureRender(commandBuffer);
ASSERT(encoders.render);
lastPipeline->Encode(encoders.render);
}
}
@ -420,7 +480,7 @@ namespace metal {
if (lastPipeline->IsCompute()) {
encoders.EnsureCompute(commandBuffer);
} else {
encoders.EnsureRender(commandBuffer);
ASSERT(encoders.render);
}
// TODO(kainino@chromium.org): Maintain buffers and offsets arrays in BindGroup so that we
@ -558,7 +618,7 @@ namespace metal {
mtlOffsets[i] = offsets[i];
}
encoders.EnsureRender(commandBuffer);
ASSERT(encoders.render);
[encoders.render
setVertexBuffers:mtlBuffers.data()
offsets:mtlOffsets.data()
@ -662,6 +722,15 @@ namespace metal {
return mtlVertexDescriptor;
}
// Framebuffer
Framebuffer::Framebuffer(Device* device, FramebufferBuilder* builder)
: FramebufferBase(builder), device(device) {
}
Framebuffer::~Framebuffer() {
}
// Pipeline
Pipeline::Pipeline(Device* device, PipelineBuilder* builder)
@ -842,6 +911,15 @@ namespace metal {
[commandBuffer commit];
}
// RenderPass
RenderPass::RenderPass(Device* device, RenderPassBuilder* builder)
: RenderPassBase(builder), device(device) {
}
RenderPass::~RenderPass() {
}
// Sampler
MTLSamplerMinMagFilter FilterModeToMinMagFilter(nxt::FilterMode mode) {

View File

@ -60,6 +60,17 @@ namespace opengl {
while(commands.NextCommandId(&type)) {
switch (type) {
case Command::AdvanceSubpass:
{
// TODO(kainino@chromium.org): implement
}
break;
case Command::BeginRenderPass:
{
// TODO(kainino@chromium.org): implement
}
break;
case Command::CopyBufferToTexture:
{
@ -124,6 +135,12 @@ namespace opengl {
}
break;
case Command::EndRenderPass:
{
// TODO(kainino@chromium.org): implement
}
break;
case Command::SetPipeline:
{
SetPipelineCmd* cmd = commands.NextCommand<SetPipelineCmd>();

View File

@ -63,6 +63,9 @@ namespace opengl {
InputStateBase* Device::CreateInputState(InputStateBuilder* builder) {
return new InputState(this, builder);
}
FramebufferBase* Device::CreateFramebuffer(FramebufferBuilder* builder) {
return new Framebuffer(this, builder);
}
PipelineBase* Device::CreatePipeline(PipelineBuilder* builder) {
return new Pipeline(this, builder);
}
@ -72,6 +75,9 @@ namespace opengl {
QueueBase* Device::CreateQueue(QueueBuilder* builder) {
return new Queue(this, builder);
}
RenderPassBase* Device::CreateRenderPass(RenderPassBuilder* builder) {
return new RenderPass(this, builder);
}
SamplerBase* Device::CreateSampler(SamplerBuilder* builder) {
return new Sampler(this, builder);
}
@ -165,6 +171,12 @@ namespace opengl {
return vertexArrayObject;
}
// Framebuffer
Framebuffer::Framebuffer(Device* device, FramebufferBuilder* builder)
: FramebufferBase(builder), device(device) {
}
// Queue
Queue::Queue(Device* device, QueueBuilder* builder)
@ -177,5 +189,11 @@ namespace opengl {
}
}
// RenderPass
RenderPass::RenderPass(Device* device, RenderPassBuilder* builder)
: RenderPassBase(builder), device(device) {
}
}
}

View File

@ -21,8 +21,10 @@
#include "common/BindGroup.h"
#include "common/BindGroupLayout.h"
#include "common/Device.h"
#include "common/Framebuffer.h"
#include "common/InputState.h"
#include "common/Queue.h"
#include "common/RenderPass.h"
#include "common/ToBackend.h"
#include "glad/glad.h"
@ -43,6 +45,8 @@ namespace opengl {
class ShaderModule;
class Texture;
class TextureView;
class Framebuffer;
class RenderPass;
struct OpenGLBackendTraits {
using BindGroupType = BindGroup;
@ -58,6 +62,8 @@ namespace opengl {
using ShaderModuleType = ShaderModule;
using TextureType = Texture;
using TextureViewType = TextureView;
using FramebufferType = Framebuffer;
using RenderPassType = RenderPass;
};
template<typename T>
@ -74,9 +80,11 @@ namespace opengl {
BufferViewBase* CreateBufferView(BufferViewBuilder* builder) override;
CommandBufferBase* CreateCommandBuffer(CommandBufferBuilder* builder) override;
InputStateBase* CreateInputState(InputStateBuilder* builder) override;
FramebufferBase* CreateFramebuffer(FramebufferBuilder* builder) override;
PipelineBase* CreatePipeline(PipelineBuilder* builder) override;
PipelineLayoutBase* CreatePipelineLayout(PipelineLayoutBuilder* builder) override;
QueueBase* CreateQueue(QueueBuilder* builder) override;
RenderPassBase* CreateRenderPass(RenderPassBuilder* builder) override;
SamplerBase* CreateSampler(SamplerBuilder* builder) override;
ShaderModuleBase* CreateShaderModule(ShaderModuleBuilder* builder) override;
TextureBase* CreateTexture(TextureBuilder* builder) override;
@ -124,6 +132,14 @@ namespace opengl {
Device* device;
};
class Framebuffer : public FramebufferBase {
public:
Framebuffer(Device* device, FramebufferBuilder* builder);
private:
Device* device;
};
class InputState : public InputStateBase {
public:
InputState(Device* device, InputStateBuilder* builder);
@ -145,6 +161,14 @@ namespace opengl {
Device* device;
};
class RenderPass : public RenderPassBase {
public:
RenderPass(Device* device, RenderPassBuilder* builder);
private:
Device* device;
};
}
}