mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-08-17 17:31:47 +00:00
This patch implements "store and MSAA resolve" store operation on Metal drivers that don't support MTLStoreActionStoreAndMultisampleResolve with a workaround that does MSAA resolve in another render pass. Driver workaround is one type of Dawn Toggles. Dawn Toggles will include other optional optimizations and features that can be configured to use or not when we create Dawn Devices. As all Metal try bots don't need this toggle, to better test this patch on the try bots: 1. We add the support of forcing enabling a workaround when starting an Dawn end2end test so that we can test the workaround on the platforms where the workaround is disabled. 2. We add an optional parameter DeviceDescriptor to CreateDevice() so that we can custom the toggles the Dawn device should use. This patch also adds the support of querying toggle details from Instance and the names of the toggles in use from Device. These APIs are tested in the Dawn unittests added in this patch. BUG=dawn:56 TEST=dawn_end2end_tests TEST=dawn_unittests Change-Id: Iae31d2ded6057eee638b6099d3061e9d78b04d55 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/6620 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Austin Eng <enga@chromium.org> Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
202 lines
6.5 KiB
C++
202 lines
6.5 KiB
C++
// Copyright 2018 The Dawn 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 <gtest/gtest.h>
|
|
|
|
#include "dawn_native/null/DeviceNull.h"
|
|
|
|
using namespace dawn_native;
|
|
|
|
namespace {
|
|
|
|
size_t ValidateValidUploadHandle(const UploadHandle& uploadHandle) {
|
|
ASSERT(uploadHandle.mappedBuffer != nullptr);
|
|
return uploadHandle.startOffset;
|
|
}
|
|
|
|
void ValidateInvalidUploadHandle(const UploadHandle& uploadHandle) {
|
|
ASSERT_EQ(uploadHandle.mappedBuffer, nullptr);
|
|
}
|
|
} // namespace
|
|
|
|
class RingBufferTests : public testing::Test {
|
|
protected:
|
|
void SetUp() override {
|
|
// TODO(bryan.bernhart@intel.com): Create this device through the adapter.
|
|
mDevice = std::make_unique<null::Device>(/*adapter*/ nullptr, /*deviceDescriptor*/ nullptr);
|
|
}
|
|
|
|
null::Device* GetDevice() const {
|
|
return mDevice.get();
|
|
}
|
|
|
|
std::unique_ptr<RingBuffer> CreateRingBuffer(size_t size) {
|
|
std::unique_ptr<RingBuffer> ringBuffer = std::make_unique<RingBuffer>(mDevice.get(), size);
|
|
DAWN_UNUSED(ringBuffer->Initialize());
|
|
return ringBuffer;
|
|
}
|
|
|
|
private:
|
|
std::unique_ptr<null::Device> mDevice;
|
|
};
|
|
|
|
// Number of basic tests for Ringbuffer
|
|
TEST_F(RingBufferTests, BasicTest) {
|
|
constexpr size_t sizeInBytes = 64000;
|
|
std::unique_ptr<RingBuffer> buffer = CreateRingBuffer(sizeInBytes);
|
|
|
|
// Ensure no requests exist on empty buffer.
|
|
EXPECT_TRUE(buffer->Empty());
|
|
|
|
ASSERT_EQ(buffer->GetSize(), sizeInBytes);
|
|
|
|
// Ensure failure upon sub-allocating an oversized request.
|
|
ValidateInvalidUploadHandle(buffer->SubAllocate(sizeInBytes + 1));
|
|
|
|
// Fill the entire buffer with two requests of equal size.
|
|
ValidateValidUploadHandle(buffer->SubAllocate(sizeInBytes / 2));
|
|
ValidateValidUploadHandle(buffer->SubAllocate(sizeInBytes / 2));
|
|
|
|
// Ensure the buffer is full.
|
|
ValidateInvalidUploadHandle(buffer->SubAllocate(1));
|
|
}
|
|
|
|
// Tests that several ringbuffer allocations do not fail.
|
|
TEST_F(RingBufferTests, RingBufferManyAlloc) {
|
|
constexpr size_t maxNumOfFrames = 64000;
|
|
constexpr size_t frameSizeInBytes = 4;
|
|
|
|
std::unique_ptr<RingBuffer> buffer = CreateRingBuffer(maxNumOfFrames * frameSizeInBytes);
|
|
|
|
size_t offset = 0;
|
|
for (size_t i = 0; i < maxNumOfFrames; ++i) {
|
|
offset = ValidateValidUploadHandle(buffer->SubAllocate(frameSizeInBytes));
|
|
GetDevice()->Tick();
|
|
ASSERT_EQ(offset, i * frameSizeInBytes);
|
|
}
|
|
}
|
|
|
|
// Tests ringbuffer sub-allocations of the same serial are correctly tracked.
|
|
TEST_F(RingBufferTests, AllocInSameFrame) {
|
|
constexpr size_t maxNumOfFrames = 3;
|
|
constexpr size_t frameSizeInBytes = 4;
|
|
|
|
std::unique_ptr<RingBuffer> buffer = CreateRingBuffer(maxNumOfFrames * frameSizeInBytes);
|
|
|
|
// F1
|
|
// [xxxx|--------]
|
|
|
|
ValidateValidUploadHandle(buffer->SubAllocate(frameSizeInBytes));
|
|
GetDevice()->Tick();
|
|
|
|
// F1 F2
|
|
// [xxxx|xxxx|----]
|
|
|
|
ValidateValidUploadHandle(buffer->SubAllocate(frameSizeInBytes));
|
|
|
|
// F1 F2
|
|
// [xxxx|xxxxxxxx]
|
|
|
|
size_t offset = ValidateValidUploadHandle(buffer->SubAllocate(frameSizeInBytes));
|
|
|
|
ASSERT_EQ(offset, 8u);
|
|
ASSERT_EQ(buffer->GetUsedSize(), frameSizeInBytes * 3);
|
|
|
|
buffer->Tick(1);
|
|
|
|
// Used size does not change as previous sub-allocations were not tracked.
|
|
ASSERT_EQ(buffer->GetUsedSize(), frameSizeInBytes * 3);
|
|
|
|
buffer->Tick(2);
|
|
|
|
ASSERT_EQ(buffer->GetUsedSize(), 0u);
|
|
EXPECT_TRUE(buffer->Empty());
|
|
}
|
|
|
|
// Tests ringbuffer sub-allocation at various offsets.
|
|
TEST_F(RingBufferTests, RingBufferSubAlloc) {
|
|
constexpr size_t maxNumOfFrames = 10;
|
|
constexpr size_t frameSizeInBytes = 4;
|
|
|
|
std::unique_ptr<RingBuffer> buffer = CreateRingBuffer(maxNumOfFrames * frameSizeInBytes);
|
|
|
|
// Sub-alloc the first eight frames.
|
|
for (size_t i = 0; i < 8; ++i) {
|
|
ValidateValidUploadHandle(buffer->SubAllocate(frameSizeInBytes));
|
|
buffer->Track();
|
|
GetDevice()->Tick();
|
|
}
|
|
|
|
// Each frame corrresponds to the serial number (for simplicity).
|
|
//
|
|
// F1 F2 F3 F4 F5 F6 F7 F8
|
|
// [xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|--------]
|
|
//
|
|
|
|
// Ensure an oversized allocation fails (only 8 bytes left)
|
|
ValidateInvalidUploadHandle(buffer->SubAllocate(frameSizeInBytes * 3));
|
|
ASSERT_EQ(buffer->GetUsedSize(), frameSizeInBytes * 8);
|
|
|
|
// Reclaim the first 3 frames.
|
|
buffer->Tick(3);
|
|
|
|
// F4 F5 F6 F7 F8
|
|
// [------------|xxxx|xxxx|xxxx|xxxx|xxxx|--------]
|
|
//
|
|
ASSERT_EQ(buffer->GetUsedSize(), frameSizeInBytes * 5);
|
|
|
|
// Re-try the over-sized allocation.
|
|
size_t offset = ValidateValidUploadHandle(buffer->SubAllocate(frameSizeInBytes * 3));
|
|
|
|
// F9 F4 F5 F6 F7 F8
|
|
// [xxxxxxxxxxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxxxxxx]
|
|
// ^^^^^^^^ wasted
|
|
|
|
// In this example, Tick(8) could not reclaim the wasted bytes. The wasted bytes
|
|
// were add to F9's sub-allocation.
|
|
// TODO(bryan.bernhart@intel.com): Decide if Tick(8) should free these wasted bytes.
|
|
|
|
ASSERT_EQ(offset, 0u);
|
|
ASSERT_EQ(buffer->GetUsedSize(), frameSizeInBytes * maxNumOfFrames);
|
|
|
|
// Ensure we are full.
|
|
ValidateInvalidUploadHandle(buffer->SubAllocate(frameSizeInBytes));
|
|
|
|
// Reclaim the next two frames.
|
|
buffer->Tick(5);
|
|
|
|
// F9 F4 F5 F6 F7 F8
|
|
// [xxxxxxxxxxxx|----|----|xxxx|xxxx|xxxx|xxxxxxxx]
|
|
//
|
|
ASSERT_EQ(buffer->GetUsedSize(), frameSizeInBytes * 8);
|
|
|
|
// Sub-alloc the chunk in the middle.
|
|
offset = ValidateValidUploadHandle(buffer->SubAllocate(frameSizeInBytes * 2));
|
|
|
|
ASSERT_EQ(offset, frameSizeInBytes * 3);
|
|
ASSERT_EQ(buffer->GetUsedSize(), frameSizeInBytes * maxNumOfFrames);
|
|
|
|
// F9 F6 F7 F8
|
|
// [xxxxxxxxxxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxxxxxx]
|
|
// ^^^^^^^^^ untracked
|
|
|
|
// Ensure we are full.
|
|
ValidateInvalidUploadHandle(buffer->SubAllocate(frameSizeInBytes));
|
|
|
|
// Reclaim all.
|
|
buffer->Tick(maxNumOfFrames);
|
|
|
|
EXPECT_TRUE(buffer->Empty());
|
|
}
|