diff --git a/impeller/fixtures/dart_tests.dart b/impeller/fixtures/dart_tests.dart index 38ebe21b01367..19596fed70375 100644 --- a/impeller/fixtures/dart_tests.dart +++ b/impeller/fixtures/dart_tests.dart @@ -196,6 +196,48 @@ gpu.RenderPipeline createUnlitRenderPipeline() { return gpu.gpuContext.createRenderPipeline(vertex!, fragment!); } +gpu.RenderPass createRenderPass() { + final gpu.Texture? renderTexture = + gpu.gpuContext.createTexture(gpu.StorageMode.devicePrivate, 100, 100); + assert(renderTexture != null); + + final gpu.CommandBuffer commandBuffer = gpu.gpuContext.createCommandBuffer(); + + final gpu.RenderTarget renderTarget = gpu.RenderTarget.singleColor( + gpu.ColorAttachment(texture: renderTexture!), + ); + return commandBuffer.createRenderPass(renderTarget); +} + +@pragma('vm:entry-point') +void uniformBindFailsForInvalidHostBufferOffset() { + final gpu.RenderPass encoder = createRenderPass(); + + final gpu.RenderPipeline pipeline = createUnlitRenderPipeline(); + encoder.bindPipeline(pipeline); + + final gpu.HostBuffer transients = gpu.HostBuffer(); + final gpu.BufferView vertInfoData = transients.emplace(float32([ + 1, 0, 0, 0, // mvp + 0, 1, 0, 0, // mvp + 0, 0, 1, 0, // mvp + 0, 0, 0, 1, // mvp + 0, 1, 0, 1, // color + ])); + final gpu.BufferView viewWithBadOffset = gpu.BufferView(vertInfoData.buffer, + offsetInBytes: 1, lengthInBytes: vertInfoData.lengthInBytes); + + final gpu.UniformSlot vertInfo = + pipeline.vertexShader.getUniformSlot('VertInfo'); + String? exception; + try { + encoder.bindUniform(vertInfo, viewWithBadOffset); + } catch (e) { + exception = e.toString(); + } + assert(exception!.contains('Failed to bind uniform')); +} + ByteData float32(List values) { return Float32List.fromList(values).buffer.asByteData(); } diff --git a/impeller/renderer/renderer_dart_unittests.cc b/impeller/renderer/renderer_dart_unittests.cc index 3e7f46b29b137..d23211950bf0b 100644 --- a/impeller/renderer/renderer_dart_unittests.cc +++ b/impeller/renderer/renderer_dart_unittests.cc @@ -145,6 +145,7 @@ DART_TEST_CASE(textureAsImageThrowsWhenNotShaderReadable); DART_TEST_CASE(canCreateShaderLibrary); DART_TEST_CASE(canReflectUniformStructs); +DART_TEST_CASE(uniformBindFailsForInvalidHostBufferOffset); DART_TEST_CASE(canCreateRenderPassAndSubmit); diff --git a/lib/gpu/host_buffer.cc b/lib/gpu/host_buffer.cc index b7edccf5318d6..e7600e8d98592 100644 --- a/lib/gpu/host_buffer.cc +++ b/lib/gpu/host_buffer.cc @@ -25,9 +25,16 @@ size_t HostBuffer::EmplaceBytes(const tonic::DartByteData& byte_data) { auto view = host_buffer_->Emplace(byte_data.data(), byte_data.length_in_bytes(), impeller::DefaultUniformAlignment()); + emplacements_[current_offset_] = view; + current_offset_ += view.range.length; return view.range.offset; } +std::optional HostBuffer::GetBufferViewForOffset( + size_t offset) { + return emplacements_[offset]; +} + } // namespace gpu } // namespace flutter diff --git a/lib/gpu/host_buffer.h b/lib/gpu/host_buffer.h index 6066166c922c6..7b25f5182c721 100644 --- a/lib/gpu/host_buffer.h +++ b/lib/gpu/host_buffer.h @@ -7,6 +7,7 @@ #include "flutter/lib/gpu/export.h" #include "flutter/lib/ui/dart_wrapper.h" +#include "impeller/core/buffer_view.h" #include "impeller/core/host_buffer.h" #include "third_party/tonic/typed_data/dart_byte_data.h" @@ -26,8 +27,12 @@ class HostBuffer : public RefCountedDartWrappable { size_t EmplaceBytes(const tonic::DartByteData& byte_data); + std::optional GetBufferViewForOffset(size_t offset); + private: + size_t current_offset_ = 0; std::shared_ptr host_buffer_; + std::unordered_map emplacements_; FML_DISALLOW_COPY_AND_ASSIGN(HostBuffer); }; diff --git a/lib/gpu/render_pass.cc b/lib/gpu/render_pass.cc index 5ea10486a579c..a1b0fd8fe7606 100644 --- a/lib/gpu/render_pass.cc +++ b/lib/gpu/render_pass.cc @@ -230,13 +230,13 @@ void InternalFlutterGpu_RenderPass_BindPipeline( template static void BindVertexBuffer(flutter::gpu::RenderPass* wrapper, - TBuffer* buffer, + TBuffer buffer, int offset_in_bytes, int length_in_bytes, int vertex_count) { auto& vertex_buffer = wrapper->GetVertexBuffer(); vertex_buffer.vertex_buffer = impeller::BufferView{ - .buffer = buffer->GetBuffer(), + .buffer = buffer, .range = impeller::Range(offset_in_bytes, length_in_bytes), }; // If the index type is set, then the `vertex_count` becomes the index @@ -258,8 +258,8 @@ void InternalFlutterGpu_RenderPass_BindVertexBufferDevice( int offset_in_bytes, int length_in_bytes, int vertex_count) { - BindVertexBuffer(wrapper, device_buffer, offset_in_bytes, length_in_bytes, - vertex_count); + BindVertexBuffer(wrapper, device_buffer->GetBuffer(), offset_in_bytes, + length_in_bytes, vertex_count); } void InternalFlutterGpu_RenderPass_BindVertexBufferHost( @@ -268,20 +268,28 @@ void InternalFlutterGpu_RenderPass_BindVertexBufferHost( int offset_in_bytes, int length_in_bytes, int vertex_count) { - BindVertexBuffer(wrapper, host_buffer, offset_in_bytes, length_in_bytes, - vertex_count); + std::optional view = + host_buffer->GetBufferViewForOffset(offset_in_bytes); + if (!view.has_value()) { + FML_LOG(ERROR) + << "Failed to bind vertex buffer due to invalid HostBuffer offset: " + << offset_in_bytes; + return; + } + BindVertexBuffer(wrapper, view->buffer, view->range.offset, + view->range.length, vertex_count); } template static void BindIndexBuffer(flutter::gpu::RenderPass* wrapper, - TBuffer* buffer, + TBuffer buffer, int offset_in_bytes, int length_in_bytes, int index_type, int index_count) { auto& vertex_buffer = wrapper->GetVertexBuffer(); vertex_buffer.index_buffer = impeller::BufferView{ - .buffer = buffer->GetBuffer(), + .buffer = buffer, .range = impeller::Range(offset_in_bytes, length_in_bytes), }; vertex_buffer.index_type = flutter::gpu::ToImpellerIndexType(index_type); @@ -295,8 +303,8 @@ void InternalFlutterGpu_RenderPass_BindIndexBufferDevice( int length_in_bytes, int index_type, int index_count) { - BindIndexBuffer(wrapper, device_buffer, offset_in_bytes, length_in_bytes, - index_type, index_count); + BindIndexBuffer(wrapper, device_buffer->GetBuffer(), offset_in_bytes, + length_in_bytes, index_type, index_count); } void InternalFlutterGpu_RenderPass_BindIndexBufferHost( @@ -306,7 +314,14 @@ void InternalFlutterGpu_RenderPass_BindIndexBufferHost( int length_in_bytes, int index_type, int index_count) { - BindIndexBuffer(wrapper, host_buffer, offset_in_bytes, length_in_bytes, + auto view = host_buffer->GetBufferViewForOffset(offset_in_bytes); + if (!view.has_value()) { + FML_LOG(ERROR) + << "Failed to bind index buffer due to invalid HostBuffer offset: " + << offset_in_bytes; + return; + } + BindIndexBuffer(wrapper, view->buffer, view->range.offset, view->range.length, index_type, index_count); } @@ -314,7 +329,7 @@ template static bool BindUniform(flutter::gpu::RenderPass* wrapper, flutter::gpu::Shader* shader, Dart_Handle uniform_name_handle, - TBuffer* buffer, + TBuffer buffer, int offset_in_bytes, int length_in_bytes) { auto& command = wrapper->GetCommand(); @@ -331,7 +346,7 @@ static bool BindUniform(flutter::gpu::RenderPass* wrapper, return command.BindResource( shader->GetShaderStage(), uniform_struct->slot, uniform_struct->metadata, impeller::BufferView{ - .buffer = buffer->GetBuffer(), + .buffer = buffer, .range = impeller::Range(offset_in_bytes, length_in_bytes), }); } @@ -343,8 +358,9 @@ bool InternalFlutterGpu_RenderPass_BindUniformDevice( flutter::gpu::DeviceBuffer* device_buffer, int offset_in_bytes, int length_in_bytes) { - return BindUniform(wrapper, shader, uniform_name_handle, device_buffer, - offset_in_bytes, length_in_bytes); + return BindUniform(wrapper, shader, uniform_name_handle, + device_buffer->GetBuffer(), offset_in_bytes, + length_in_bytes); } bool InternalFlutterGpu_RenderPass_BindUniformHost( @@ -354,8 +370,15 @@ bool InternalFlutterGpu_RenderPass_BindUniformHost( flutter::gpu::HostBuffer* host_buffer, int offset_in_bytes, int length_in_bytes) { - return BindUniform(wrapper, shader, uniform_name_handle, host_buffer, - offset_in_bytes, length_in_bytes); + auto view = host_buffer->GetBufferViewForOffset(offset_in_bytes); + if (!view.has_value()) { + FML_LOG(ERROR) + << "Failed to bind index buffer due to invalid HostBuffer offset: " + << offset_in_bytes; + return false; + } + return BindUniform(wrapper, shader, uniform_name_handle, view->buffer, + view->range.offset, view->range.length); } bool InternalFlutterGpu_RenderPass_BindTexture(