Fix Oversized D3D11 Immediate Uniform Buffer kMaxImmediateSizeD3D11 was defined as sizeof(RenderImmediates / ComputeImmediates), i.e. a byte count, but every use treats it as a number of uint32_t elements: it sizes std::array<uint32_t, N>, bounds the element offset, and multiplies by sizeof(uint32_t) for the GPU buffer byte size. As a result the CPU array and the GPU uniform buffer were 4x larger than needed. Rename the constant to kMaxImmediateSlotsD3D11 and convert the maximum immediate struct byte size to uint32_t slots with Align, so the buffer stays correctly sized even if a struct size is not a multiple of four. Make IsPowerOfTwo constexpr so Align can be used in constant expressions, and add compile-time coverage for both helpers. Bug: 366291600 Change-Id: I7375650f0de766b154f72e8ef4f70ba266d5248a Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/329755 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Quyen Le <lehoangquyen@google.com>
diff --git a/src/dawn/common/Math.cpp b/src/dawn/common/Math.cpp index a6826b9..9579482 100644 --- a/src/dawn/common/Math.cpp +++ b/src/dawn/common/Math.cpp
@@ -55,11 +55,6 @@ return 1ull << (Log2(n - 1) + 1); } -bool IsPowerOfTwo(uint64_t n) { - DAWN_RELEASE_ASSUME(n != 0); - return (n & (n - 1)) == 0; -} - bool IsPtrAligned(const void* ptr, size_t alignment) { DAWN_RELEASE_ASSUME(IsPowerOfTwo(alignment)); DAWN_RELEASE_ASSUME(alignment != 0);
diff --git a/src/dawn/common/Math.h b/src/dawn/common/Math.h index 2bec112..7461d97 100644 --- a/src/dawn/common/Math.h +++ b/src/dawn/common/Math.h
@@ -50,7 +50,10 @@ // The following are not valid for 0 uint32_t Log2(uint32_t value); uint32_t Log2(uint64_t value); -bool IsPowerOfTwo(uint64_t n); +constexpr bool IsPowerOfTwo(uint64_t n) { + DAWN_RELEASE_ASSUME(n != 0); + return (n & (n - 1)) == 0; +} // Returns 2^exp for integrals template <std::integral T>
diff --git a/src/dawn/native/d3d11/CommandRecordingContextD3D11.cpp b/src/dawn/native/d3d11/CommandRecordingContextD3D11.cpp index de84c4a..aa717b4 100644 --- a/src/dawn/native/d3d11/CommandRecordingContextD3D11.cpp +++ b/src/dawn/native/d3d11/CommandRecordingContextD3D11.cpp
@@ -180,9 +180,9 @@ void ScopedCommandRecordingContext::WriteUniformBufferRange(uint32_t offset, const void* data, size_t size) const { - DAWN_ASSERT(offset < CommandRecordingContext::kMaxImmediateSizeD3D11); + DAWN_ASSERT(offset < CommandRecordingContext::kMaxImmediateSlotsD3D11); DAWN_ASSERT(size <= - sizeof(uint32_t) * (CommandRecordingContext::kMaxImmediateSizeD3D11 - offset)); + sizeof(uint32_t) * (CommandRecordingContext::kMaxImmediateSlotsD3D11 - offset)); DAWN_UNSAFE_TODO(std::memcpy(&Get()->mUniformBufferData[offset], data, size)); Get()->mUniformBufferDirty = true; } @@ -383,7 +383,7 @@ DeviceBase* device) { // Create a uniform buffer for user and internal Immediates. BufferDescriptor descriptor; - descriptor.size = sizeof(uint32_t) * kMaxImmediateSizeD3D11; + descriptor.size = sizeof(uint32_t) * kMaxImmediateSlotsD3D11; descriptor.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst; descriptor.mappedAtCreation = false; descriptor.label = "ImmediatesInternalBuffer";
diff --git a/src/dawn/native/d3d11/CommandRecordingContextD3D11.h b/src/dawn/native/d3d11/CommandRecordingContextD3D11.h index 4ee007c..a14eda6 100644 --- a/src/dawn/native/d3d11/CommandRecordingContextD3D11.h +++ b/src/dawn/native/d3d11/CommandRecordingContextD3D11.h
@@ -35,6 +35,7 @@ #include "absl/container/flat_hash_set.h" #include "absl/container/inlined_vector.h" #include "src/dawn/common/Constants.h" +#include "src/dawn/common/Math.h" #include "src/dawn/common/MutexProtected.h" #include "src/dawn/common/Ref.h" #include "src/dawn/common/StackAllocated.h" @@ -92,8 +93,10 @@ bool IsValid() const; static ResultOrError<Ref<BufferBase>> CreateInternalUniformBuffer(DeviceBase* device); - static constexpr uint32_t kMaxImmediateSizeD3D11 = - std::max(sizeof(RenderImmediates), sizeof(ComputeImmediates)); + // The number of uint32_t elements in the immediate uniform buffer. + static constexpr uint32_t kMaxImmediateSlotsD3D11 = + Align(std::max(sizeof(RenderImmediates), sizeof(ComputeImmediates)), sizeof(uint32_t)) / + sizeof(uint32_t); void ReleaseKeyedMutexes(); @@ -116,7 +119,7 @@ // The uniform buffer for built-in variables. Ref<GPUUsableBuffer> mUniformBuffer; - std::array<uint32_t, kMaxImmediateSizeD3D11> mUniformBufferData{}; + std::array<uint32_t, kMaxImmediateSlotsD3D11> mUniformBufferData{}; bool mUniformBufferDirty = true; absl::flat_hash_set<Ref<d3d::KeyedMutex>> mAcquiredKeyedMutexes;
diff --git a/src/dawn/tests/unittests/MathTests.cpp b/src/dawn/tests/unittests/MathTests.cpp index 8447c31..feb2bd8 100644 --- a/src/dawn/tests/unittests/MathTests.cpp +++ b/src/dawn/tests/unittests/MathTests.cpp
@@ -121,6 +121,9 @@ // Tests for IsPowerOfTwo TEST(Math, IsPowerOfTwo) { + static_assert(IsPowerOfTwo(1)); + static_assert(!IsPowerOfTwo(3)); + ASSERT_TRUE(IsPowerOfTwo(1)); ASSERT_TRUE(IsPowerOfTwo(2)); ASSERT_FALSE(IsPowerOfTwo(3)); @@ -160,6 +163,8 @@ // Tests for Align TEST(Math, Align) { + static_assert(Align(77u, 4) == 80u); + // 0 aligns to 0 ASSERT_EQ(Align(0u, 4), 0u); ASSERT_EQ(Align(0u, 256), 0u);