diff --git a/src/dawn/engine_dawn.cc b/src/dawn/engine_dawn.cc index 7a6aea8ba..334d005f8 100644 --- a/src/dawn/engine_dawn.cc +++ b/src/dawn/engine_dawn.cc @@ -355,11 +355,79 @@ ::dawn::SamplerDescriptor GetDefaultSamplerDescriptor() { return desc; } -// Creates a bind group layout. +// Creates a bind group. // Copied from Dawn utils source code. + +// Helpers to make creating bind groups look nicer: +// +// utils::MakeBindGroup(device, layout, { +// {0, mySampler}, +// {1, myBuffer, offset, size}, +// {3, myTexture} +// }); + +// Structure with one constructor per-type of bindings, so that the +// initializer_list accepts bindings with the right type and no extra +// information. +struct BindingInitializationHelper { + BindingInitializationHelper(uint32_t binding, const ::dawn::Sampler& sampler); + BindingInitializationHelper(uint32_t binding, + const ::dawn::TextureView& textureView); + BindingInitializationHelper(uint32_t binding, + const ::dawn::Buffer& buffer, + uint64_t offset, + uint64_t size); + + ::dawn::BindGroupBinding GetAsBinding() const; + + uint32_t binding; + ::dawn::Sampler sampler; + ::dawn::TextureView textureView; + ::dawn::Buffer buffer; + uint64_t offset = 0; + uint64_t size = 0; +}; +BindingInitializationHelper::BindingInitializationHelper( + uint32_t binding, + const ::dawn::Buffer& buffer, + uint64_t offset, + uint64_t size) + : binding(binding), buffer(buffer), offset(offset), size(size) {} + +::dawn::BindGroupBinding BindingInitializationHelper::GetAsBinding() const { + ::dawn::BindGroupBinding result; + + result.binding = binding; + result.sampler = sampler; + result.textureView = textureView; + result.buffer = buffer; + result.offset = offset; + result.size = size; + + return result; +} + +::dawn::BindGroup MakeBindGroup( + const ::dawn::Device& device, + const ::dawn::BindGroupLayout& layout, + std::vector bindingsInitializer) { + std::vector<::dawn::BindGroupBinding> bindings; + for (const BindingInitializationHelper& helper : bindingsInitializer) { + bindings.push_back(helper.GetAsBinding()); + } + + ::dawn::BindGroupDescriptor descriptor; + descriptor.layout = layout; + descriptor.bindingCount = bindings.size(); + descriptor.bindings = bindings.data(); + + return device.CreateBindGroup(&descriptor); +} + +// Creates a bind group layout. ::dawn::BindGroupLayout MakeBindGroupLayout( const ::dawn::Device& device, - std::initializer_list<::dawn::BindGroupLayoutBinding> bindingsInitializer) { + std::vector<::dawn::BindGroupLayoutBinding> bindingsInitializer) { constexpr ::dawn::ShaderStageBit kNoStages{}; std::vector<::dawn::BindGroupLayoutBinding> bindings; @@ -496,10 +564,10 @@ Result EngineDawn::CreatePipeline(::amber::Pipeline* pipeline) { auto shader = device_->CreateShaderModule(&descriptor); if (!shader) { - return Result("Dawn::SetShader: failed to create shader"); + return Result("Dawn::CreatePipeline: failed to create shader"); } if (module_for_type.count(type)) { - return Result("Dawn::SetShader: module for type already exists"); + return Result("Dawn::CreatePipeline: module for type already exists"); } module_for_type[type] = shader; } @@ -530,6 +598,7 @@ Result EngineDawn::CreatePipeline(::amber::Pipeline* pipeline) { } pipeline_map_[pipeline].render_pipeline.reset( new RenderPipelineInfo(pipeline, vs, fs)); + CreateFramebufferIfNeeded(pipeline_map_[pipeline].render_pipeline.get()); break; } } @@ -571,19 +640,13 @@ Result EngineDawn::DoClear(const ClearCommand* command) { if (!render_pipeline) return Result("Clear invoked on invalid or missing render pipeline"); - // TODO(dneto): Likely, we can create the render objects during - // CreatePipeline. - Result result = CreateFramebufferIfNeeded(render_pipeline); - if (!result.IsSuccess()) - return result; - // Record a render pass in a command on the command buffer. // // First describe the color attachments, and how they are initialized // via the load op. The load op is "clear" to the clear colour. ::dawn::RenderPassColorAttachmentDescriptor color_attachment = ::dawn::RenderPassColorAttachmentDescriptor(); - color_attachment.attachment = render_pipeline->fb_texture.CreateDefaultView(); + color_attachment.attachment = render_pipeline->textureView; color_attachment.resolveTarget = nullptr; color_attachment.clearColor = render_pipeline->clear_color_value; color_attachment.loadOp = ::dawn::LoadOp::Clear; @@ -659,7 +722,12 @@ Result DawnPipelineHelper::CreateRenderPipelineDescriptor( depth_stencil_format = ::dawn::TextureFormat::D32FloatS8Uint; } - renderPipelineDescriptor.layout = MakeBasicPipelineLayout(device, nullptr); + if (render_pipeline.hasBinding) + renderPipelineDescriptor.layout = + MakeBasicPipelineLayout(device, &render_pipeline.bindGroupLayout); + else + renderPipelineDescriptor.layout = MakeBasicPipelineLayout(device, nullptr); + renderPipelineDescriptor.primitiveTopology = ::dawn::PrimitiveTopology::TriangleList; renderPipelineDescriptor.sampleCount = 1; @@ -750,10 +818,10 @@ Result DawnPipelineHelper::CreateRenderPassDescriptor( const RenderPipelineInfo& render_pipeline, const ::dawn::Device& device) { std::initializer_list<::dawn::TextureView> colorAttachmentInfo = { - render_pipeline.fb_texture.CreateDefaultView()}; + render_pipeline.textureView}; for (uint32_t i = 0; i < kMaxColorAttachments; ++i) { - colorAttachmentsInfo[i].loadOp = ::dawn::LoadOp::Clear; + colorAttachmentsInfo[i].loadOp = ::dawn::LoadOp::Load; colorAttachmentsInfo[i].storeOp = ::dawn::StoreOp::Store; colorAttachmentsInfo[i].clearColor = render_pipeline.clear_color_value; colorAttachmentsInfoPtr[i] = nullptr; @@ -808,10 +876,7 @@ Result DawnPipelineHelper::CreateRenderPassDescriptor( Result EngineDawn::DoDrawRect(const DrawRectCommand* command) { RenderPipelineInfo* render_pipeline = GetRenderPipeline(command); if (!render_pipeline) - return Result("Clear invoked on invalid or missing render pipeline"); - Result result = CreateFramebufferIfNeeded(render_pipeline); - if (!result.IsSuccess()) - return result; + return Result("DrawRect invoked on invalid or missing render pipeline"); float x = command->GetX(); float y = command->GetY(); @@ -835,25 +900,27 @@ Result EngineDawn::DoDrawRect(const DrawRectCommand* command) { ::dawn::Buffer indexBuffer = CreateBufferFromData( *device_, indexData, sizeof(indexData), ::dawn::BufferUsageBit::Index); - std::vector values(8); - // Bottom left - values[0].SetDoubleValue(static_cast(x)); - values[1].SetDoubleValue(static_cast(y + rectangleHeight)); - // Top left - values[2].SetDoubleValue(static_cast(x)); - values[3].SetDoubleValue(static_cast(y)); - // Top right - values[4].SetDoubleValue(static_cast(x + rectangleWidth)); - values[5].SetDoubleValue(static_cast(y)); - // Bottom right - values[6].SetDoubleValue(static_cast(x + rectangleWidth)); - values[7].SetDoubleValue(static_cast(y + rectangleHeight)); - - static const float vertexData[4 * 4] = { - values[0].AsFloat(), values[1].AsFloat(), 0.0f, 1.0f, - values[2].AsFloat(), values[3].AsFloat(), 0.0f, 1.0f, - values[4].AsFloat(), values[5].AsFloat(), 0.0f, 1.0f, - values[6].AsFloat(), values[7].AsFloat(), 0.0f, 1.0f, + const float vertexData[4 * 4] = { + // Bottom left + x, + y + rectangleHeight, + 0.0f, + 1.0f, + // Top left + x, + y, + 0.0f, + 1.0f, + // Top right + x + rectangleWidth, + y, + 0.0f, + 1.0f, + // Bottom right + x + rectangleWidth, + y + rectangleHeight, + 0.0f, + 1.0f, }; ::dawn::Buffer vertexBuffer = CreateBufferFromData( @@ -873,7 +940,11 @@ Result EngineDawn::DoDrawRect(const DrawRectCommand* command) { ::dawn::CommandEncoder encoder = device_->CreateCommandEncoder(); ::dawn::RenderPassEncoder pass = encoder.BeginRenderPass(renderPassDescriptor); + pass.SetPipeline(pipeline); + if (render_pipeline->hasBinding) { + pass.SetBindGroup(0, render_pipeline->bindGroup, 0, nullptr); + } pass.SetVertexBuffers(0, 1, &vertexBuffer, vertexBufferOffsets); pass.SetIndexBuffer(indexBuffer, 0); pass.DrawIndexed(6, 1, 0, 0, 0); @@ -905,8 +976,75 @@ Result EngineDawn::DoPatchParameterVertices( return Result("Dawn:DoPatch not implemented"); } +// ::dawn::CommandBuffer CreateSimpleComputeCommandBuffer( +// const dawn::ComputePipeline& pipeline, const dawn::BindGroup& +// bindGroup) +// { +// dawn::CommandEncoder encoder = device.CreateCommandEncoder(); +// dawn::ComputePassEncoder pass = encoder.BeginComputePass(); +// pass.SetPipeline(pipeline); +// pass.SetBindGroup(0, bindGroup, 0, nullptr); +// pass.Dispatch(1, 1, 1); +// pass.EndPass(); +// return encoder.Finish(); +// } + Result EngineDawn::DoBuffer(const BufferCommand*) { return Result("Dawn:DoBuffer not implemented"); +#if 0 + // TODO(SarahM0): it can be a compute pipeline too + RenderPipelineInfo* render_pipeline = GetRenderPipeline(command); + if (!render_pipeline) + return Result("DoBuffer invoked on invalid or missing render pipeline"); + Result result = CreateFramebufferIfNeeded(render_pipeline); + if (!result.IsSuccess()) + return result; + + if (!command->IsSSBO() && !command->IsUniform()) + return Result("EngineDawn::DoBuffer not supported buffer type"); + + const std::vector values = command->GetValues(); + std::vector fValues; + float* f = new float[values.size()]; + for (uint i = 0; i < values.size(); i++) + f[i] = values[i].AsFloat(); + + ::dawn::Buffer buffer; + if (command->IsSSBO()) + buffer = CreateBufferFromData(*device_, f, sizeof(f), + ::dawn::BufferUsageBit::Storage | + ::dawn::BufferUsageBit::TransferSrc | + ::dawn::BufferUsageBit::TransferDst); + else if (command->IsUniform()) + buffer = CreateBufferFromData(*device_, f, sizeof(f), + ::dawn::BufferUsageBit::Uniform | + ::dawn::BufferUsageBit::TransferSrc | + ::dawn::BufferUsageBit::TransferDst); + else + return Result("EngineDawn::DoBuffer not supported buffer type"); + + ::dawn::ShaderStageBit kAllStages = + ::dawn::ShaderStageBit::Vertex | ::dawn::ShaderStageBit::Fragment; + + render_pipeline->bindGroupLayout = MakeBindGroupLayout( + *device_, { + {0, kAllStages, ::dawn::BindingType::StorageBuffer}, + }); + + render_pipeline->hasBinding = true; + + render_pipeline->bindGroup = + MakeBindGroup(*device_, render_pipeline->bindGroupLayout, + { + { + command->GetBinding(), + buffer, + 0, + sizeof(f), + }, + }); + return {}; +#endif } Result EngineDawn::CreateFramebufferIfNeeded( @@ -918,26 +1056,49 @@ Result EngineDawn::CreateFramebufferIfNeeded( const uint32_t width = render_pipeline->pipeline->GetFramebufferWidth(); const uint32_t height = render_pipeline->pipeline->GetFramebufferHeight(); - // TODO(dneto): For now, assume color attachment 0 is the framebuffer. + for (auto colorAttachment : + render_pipeline->pipeline->GetColorAttachments()) { + auto* amber_format = colorAttachment.buffer->GetFormat(); + if (!amber_format) + return Result("Color attachment 0 has no format!"); + ::dawn::TextureFormat fb_format{}; + result = GetDawnTextureFormat(*amber_format, &fb_format); + if (!result.IsSuccess()) + return result; + + uint32_t location; + render_pipeline->pipeline->GetLocationForColorAttachment( + colorAttachment.buffer, &location); + if (colorAttachmentLocationToTextureView_map_.find(location) == + colorAttachmentLocationToTextureView_map_.end()) { + ::dawn::Texture fb_texture; + result = MakeTexture(*device_, fb_format, width, height, &fb_texture); + if (!result.IsSuccess()) + return result; + render_pipeline->fb_texture = std::move(fb_texture); + colorAttachmentLocationToTexture_map_[location] = + &render_pipeline->fb_texture; + render_pipeline->textureView = + render_pipeline->fb_texture.CreateDefaultView(); + colorAttachmentLocationToTextureView_map_[location] = + &render_pipeline->textureView; + } else { + render_pipeline->textureView = + *colorAttachmentLocationToTextureView_map_[location]; + render_pipeline->fb_texture = + *colorAttachmentLocationToTexture_map_[location]; + } + } + auto* amber_format = render_pipeline->pipeline->GetColorAttachments()[0].buffer->GetFormat(); if (!amber_format) return Result("Color attachment 0 has no format!"); - ::dawn::TextureFormat fb_format{}; result = GetDawnTextureFormat(*amber_format, &fb_format); if (!result.IsSuccess()) return result; - { - ::dawn::Texture fb_texture; - - result = MakeTexture(*device_, fb_format, width, height, &fb_texture); - if (!result.IsSuccess()) - return result; - render_pipeline->fb_texture = std::move(fb_texture); - } - // After that, only create the Dawn depth-stencil texture if the Amber // depth-stencil texture exists. auto* depthBuffer = render_pipeline->pipeline->GetDepthBuffer().buffer; @@ -982,6 +1143,67 @@ Result EngineDawn::CreateFramebufferIfNeeded( render_pipeline->fb_size = size; } + ::dawn::ShaderStageBit kAllStages = + ::dawn::ShaderStageBit::Vertex | ::dawn::ShaderStageBit::Fragment; + std::vector bindingInitalizerHelper; + std::vector<::dawn::BindGroupLayoutBinding> bindGroup; + + for (const auto& buf_info : render_pipeline->pipeline->GetBuffers()) { +#if 0 + std::cout << buf_info.buffer->ValueCount() << " "; + const auto* data = buf_info.buffer->GetValues(); + for (uint i = 0; i < buf_info.buffer->ValueCount(); i++) { + std::cout << data[i] << " "; + } + std::cout << buf_info.descriptor_set << " "; + std::cout << buf_info.binding << " "; + std::cout << "\n"; +#endif + ::dawn::BufferUsageBit bufferUsage; + ::dawn::BindingType bindingType; + switch (buf_info.buffer->GetBufferType()) { + case BufferType::kStorage: { + bufferUsage = ::dawn::BufferUsageBit::Storage; + bindingType = ::dawn::BindingType::StorageBuffer; + break; + } + case BufferType::kUniform: { + bufferUsage = ::dawn::BufferUsageBit::Uniform; + bindingType = ::dawn::BindingType::UniformBuffer; + break; + } + default: { + return Result("Dawn: CreatePipeline - unknown buffer type: " + + std::to_string(static_cast( + buf_info.buffer->GetBufferType()))); + break; + } + } + + ::dawn::Buffer buffer = + CreateBufferFromData(*device_, buf_info.buffer->ValuePtr()->data(), + buf_info.buffer->GetSizeInBytes(), + bufferUsage | ::dawn::BufferUsageBit::TransferSrc | + ::dawn::BufferUsageBit::TransferDst); + + ::dawn::BindGroupLayoutBinding bglb; + bglb.binding = buf_info.binding; + bglb.visibility = kAllStages; + bglb.type = bindingType; + bindGroup.push_back(bglb); + + BindingInitializationHelper tempBinding = BindingInitializationHelper( + buf_info.binding, buffer, 0, buf_info.buffer->GetSizeInBytes()); + bindingInitalizerHelper.push_back(tempBinding); + } + + if (bindGroup.size() > 0 && bindingInitalizerHelper.size() > 0) { + render_pipeline->bindGroupLayout = MakeBindGroupLayout(*device_, bindGroup); + render_pipeline->bindGroup = MakeBindGroup( + *device_, render_pipeline->bindGroupLayout, bindingInitalizerHelper); + render_pipeline->hasBinding = true; + } + return {}; } diff --git a/src/dawn/engine_dawn.h b/src/dawn/engine_dawn.h index 44a0c2926..d09b6fb56 100644 --- a/src/dawn/engine_dawn.h +++ b/src/dawn/engine_dawn.h @@ -83,6 +83,12 @@ class EngineDawn : public Engine { // Mapping from the generic engine's Pipeline object to our own Dawn-specific // pipelines. std::unordered_map pipeline_map_; + + std::unordered_map + colorAttachmentLocationToTextureView_map_; + + std::unordered_map + colorAttachmentLocationToTexture_map_; }; } // namespace dawn diff --git a/src/dawn/pipeline_info.h b/src/dawn/pipeline_info.h index d455d6343..c25ec07d6 100644 --- a/src/dawn/pipeline_info.h +++ b/src/dawn/pipeline_info.h @@ -30,8 +30,7 @@ namespace dawn { /// Stores information relating to a graphics pipeline in Dawn. struct RenderPipelineInfo { RenderPipelineInfo() {} - RenderPipelineInfo(::amber::Pipeline* the_pipeline, - ::dawn::ShaderModule vert, + RenderPipelineInfo(::amber::Pipeline* the_pipeline, ::dawn::ShaderModule vert, ::dawn::ShaderModule frag) : pipeline(the_pipeline), vertex_shader(vert), fragment_shader(frag) {} @@ -45,6 +44,7 @@ struct RenderPipelineInfo { /// The framebuffer color render target. This resides on the GPU. ::dawn::Texture fb_texture; + ::dawn::TextureView textureView; /// The depth and stencil target. This resides on the GPU. ::dawn::Texture depth_stencil_texture; /// The buffer to which we will copy the rendered pixel values, for @@ -61,6 +61,10 @@ struct RenderPipelineInfo { /// The number of data bytes in the framebuffer host-side buffer. uint32_t fb_size = 0; + bool hasBinding = false; + ::dawn::BindGroup bindGroup; + ::dawn::BindGroupLayout bindGroupLayout; + // TODO(dneto): Record index data // TODO(dneto): Record buffer data };