Add SwapChain::GetCurrentTexture
This is to eventually allow more using swapchain textures as CopySrc and CopyDst. Note that this commit doesn't add any additional usages. Because textures can reflect their creation parameters, swapchains now need to pass in the correct texture descriptor in all code paths. This requires additional handling in dawn::native error swapchains, and dawn::wire::client's SwapChain reservation and Device::CreateSwapChain code. Tests are added for all of these code paths except dawn::wire::client::Device::CreateSwapChain because there is no way to create a Surface in wire tests at the moment (they don't have an instance). Bug: dawn:1551 Change-Id: I22d5e909e1e94d48eb52cae57aabff8a7f0c04c1 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/133463 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Austin Eng <enga@chromium.org> Commit-Queue: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Loko Kung <lokokung@google.com>
This commit is contained in:
parent
2ef42221d4
commit
943a1a2d7a
|
@ -2576,6 +2576,7 @@
|
|||
"category": "object",
|
||||
"methods": [
|
||||
{"name": "get current texture view", "returns": "texture view"},
|
||||
{"name": "get current texture", "returns": "texture"},
|
||||
{"name": "present"}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -224,12 +224,14 @@
|
|||
"BufferUnmap",
|
||||
"DeviceCreateErrorBuffer",
|
||||
"DeviceCreateQuerySet",
|
||||
"DeviceCreateSwapChain",
|
||||
"DeviceCreateTexture",
|
||||
"DeviceCreateErrorTexture",
|
||||
"DeviceGetAdapter",
|
||||
"DeviceGetQueue",
|
||||
"DeviceGetSupportedSurfaceUsage",
|
||||
"DeviceInjectError"
|
||||
"DeviceInjectError",
|
||||
"SwapChainGetCurrentTexture"
|
||||
],
|
||||
"client_special_objects": [
|
||||
"Adapter",
|
||||
|
@ -239,6 +241,7 @@
|
|||
"QuerySet",
|
||||
"Queue",
|
||||
"ShaderModule",
|
||||
"SwapChain",
|
||||
"Texture"
|
||||
],
|
||||
"server_custom_pre_handler_commands": [
|
||||
|
|
|
@ -71,7 +71,8 @@ class DAWN_WIRE_EXPORT WireClient : public CommandHandler {
|
|||
const volatile char* HandleCommands(const volatile char* commands, size_t size) override;
|
||||
|
||||
ReservedTexture ReserveTexture(WGPUDevice device, const WGPUTextureDescriptor* descriptor);
|
||||
ReservedSwapChain ReserveSwapChain(WGPUDevice device);
|
||||
ReservedSwapChain ReserveSwapChain(WGPUDevice device,
|
||||
const WGPUSwapChainDescriptor* descriptor);
|
||||
ReservedDevice ReserveDevice();
|
||||
ReservedInstance ReserveInstance();
|
||||
|
||||
|
|
|
@ -1268,7 +1268,7 @@ SwapChainBase* DeviceBase::APICreateSwapChain(Surface* surface,
|
|||
Ref<SwapChainBase> result;
|
||||
if (ConsumedError(CreateSwapChain(surface, descriptor), &result,
|
||||
"calling %s.CreateSwapChain(%s).", this, descriptor)) {
|
||||
return SwapChainBase::MakeError(this);
|
||||
return SwapChainBase::MakeError(this, descriptor);
|
||||
}
|
||||
return result.Detach();
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "dawn/native/Device.h"
|
||||
|
@ -84,8 +85,7 @@ ApiObjectBase::~ApiObjectBase() {
|
|||
}
|
||||
|
||||
void ApiObjectBase::APISetLabel(const char* label) {
|
||||
mLabel = label;
|
||||
SetLabelImpl();
|
||||
SetLabel(label);
|
||||
}
|
||||
|
||||
void ApiObjectBase::APIRelease() {
|
||||
|
@ -97,6 +97,11 @@ void ApiObjectBase::APIRelease() {
|
|||
Release();
|
||||
}
|
||||
|
||||
void ApiObjectBase::SetLabel(std::string label) {
|
||||
mLabel = std::move(label);
|
||||
SetLabelImpl();
|
||||
}
|
||||
|
||||
const std::string& ApiObjectBase::GetLabel() const {
|
||||
return mLabel;
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@ class ApiObjectBase : public ObjectBase, public LinkNode<ApiObjectBase> {
|
|||
~ApiObjectBase() override;
|
||||
|
||||
virtual ObjectType GetType() const = 0;
|
||||
void SetLabel(std::string label);
|
||||
const std::string& GetLabel() const;
|
||||
|
||||
virtual void FormatLabel(absl::FormatSink* s) const;
|
||||
|
|
|
@ -28,7 +28,8 @@ namespace {
|
|||
|
||||
class ErrorSwapChain final : public SwapChainBase {
|
||||
public:
|
||||
explicit ErrorSwapChain(DeviceBase* device) : SwapChainBase(device, ObjectBase::kError) {}
|
||||
explicit ErrorSwapChain(DeviceBase* device, const SwapChainDescriptor* desc)
|
||||
: SwapChainBase(device, desc, ObjectBase::kError) {}
|
||||
|
||||
private:
|
||||
ResultOrError<Ref<TextureBase>> GetCurrentTextureImpl() override { UNREACHABLE(); }
|
||||
|
@ -120,12 +121,19 @@ SwapChainBase::~SwapChainBase() {
|
|||
ASSERT(!mAttached);
|
||||
}
|
||||
|
||||
SwapChainBase::SwapChainBase(DeviceBase* device, ObjectBase::ErrorTag tag)
|
||||
: ApiObjectBase(device, tag) {}
|
||||
SwapChainBase::SwapChainBase(DeviceBase* device,
|
||||
const SwapChainDescriptor* descriptor,
|
||||
ObjectBase::ErrorTag tag)
|
||||
: ApiObjectBase(device, tag),
|
||||
mWidth(descriptor->width),
|
||||
mHeight(descriptor->height),
|
||||
mFormat(descriptor->format),
|
||||
mUsage(descriptor->usage),
|
||||
mPresentMode(descriptor->presentMode) {}
|
||||
|
||||
// static
|
||||
SwapChainBase* SwapChainBase::MakeError(DeviceBase* device) {
|
||||
return new ErrorSwapChain(device);
|
||||
SwapChainBase* SwapChainBase::MakeError(DeviceBase* device, const SwapChainDescriptor* desc) {
|
||||
return new ErrorSwapChain(device, desc);
|
||||
}
|
||||
|
||||
void SwapChainBase::DestroyImpl() {}
|
||||
|
@ -154,11 +162,25 @@ void SwapChainBase::APIConfigure(wgpu::TextureFormat format,
|
|||
DAWN_VALIDATION_ERROR("Configure is invalid for surface-based swapchains."));
|
||||
}
|
||||
|
||||
TextureBase* SwapChainBase::APIGetCurrentTexture() {
|
||||
Ref<TextureBase> result;
|
||||
if (GetDevice()->ConsumedError(GetCurrentTexture(), &result, "calling %s.GetCurrentTexture()",
|
||||
this)) {
|
||||
TextureDescriptor desc = GetSwapChainBaseTextureDescriptor(this);
|
||||
TextureBase* errorTexture = TextureBase::MakeError(GetDevice(), &desc);
|
||||
SetChildLabel(errorTexture);
|
||||
return errorTexture;
|
||||
}
|
||||
return result.Detach();
|
||||
}
|
||||
|
||||
TextureViewBase* SwapChainBase::APIGetCurrentTextureView() {
|
||||
Ref<TextureViewBase> result;
|
||||
if (GetDevice()->ConsumedError(GetCurrentTextureView(), &result,
|
||||
"calling %s.GetCurrentTextureView()", this)) {
|
||||
return TextureViewBase::MakeError(GetDevice());
|
||||
TextureViewBase* errorView = TextureViewBase::MakeError(GetDevice());
|
||||
SetChildLabel(errorView);
|
||||
return errorView;
|
||||
}
|
||||
return result.Detach();
|
||||
}
|
||||
|
@ -172,6 +194,7 @@ ResultOrError<Ref<TextureBase>> SwapChainBase::GetCurrentTexture() {
|
|||
}
|
||||
|
||||
DAWN_TRY_ASSIGN(mCurrentTexture, GetCurrentTextureImpl());
|
||||
SetChildLabel(mCurrentTexture.Get());
|
||||
|
||||
// Check that the return texture matches exactly what was given for this descriptor.
|
||||
ASSERT(mCurrentTexture->GetFormat().format == mFormat);
|
||||
|
@ -258,4 +281,8 @@ MaybeError SwapChainBase::ValidateGetCurrentTexture() const {
|
|||
return {};
|
||||
}
|
||||
|
||||
void SwapChainBase::SetChildLabel(ApiObjectBase* child) const {
|
||||
child->SetLabel(absl::StrFormat("of %s", this));
|
||||
}
|
||||
|
||||
} // namespace dawn::native
|
||||
|
|
|
@ -32,7 +32,7 @@ class SwapChainBase : public ApiObjectBase {
|
|||
public:
|
||||
SwapChainBase(DeviceBase* device, Surface* surface, const SwapChainDescriptor* descriptor);
|
||||
|
||||
static SwapChainBase* MakeError(DeviceBase* device);
|
||||
static SwapChainBase* MakeError(DeviceBase* device, const SwapChainDescriptor* descriptor);
|
||||
ObjectType GetType() const override;
|
||||
|
||||
// This is called when the swapchain is detached when one of the following happens:
|
||||
|
@ -59,6 +59,7 @@ class SwapChainBase : public ApiObjectBase {
|
|||
wgpu::TextureUsage allowedUsage,
|
||||
uint32_t width,
|
||||
uint32_t height);
|
||||
TextureBase* APIGetCurrentTexture();
|
||||
TextureViewBase* APIGetCurrentTextureView();
|
||||
void APIPresent();
|
||||
|
||||
|
@ -72,11 +73,13 @@ class SwapChainBase : public ApiObjectBase {
|
|||
wgpu::BackendType GetBackendType() const;
|
||||
|
||||
protected:
|
||||
SwapChainBase(DeviceBase* device, ObjectBase::ErrorTag tag);
|
||||
SwapChainBase(DeviceBase* device, const SwapChainDescriptor* desc, ObjectBase::ErrorTag tag);
|
||||
~SwapChainBase() override;
|
||||
void DestroyImpl() override;
|
||||
|
||||
private:
|
||||
void SetChildLabel(ApiObjectBase* child) const;
|
||||
|
||||
bool mAttached = false;
|
||||
uint32_t mWidth;
|
||||
uint32_t mHeight;
|
||||
|
|
|
@ -216,7 +216,7 @@ wgpu::Device CreateCppDawnDevice() {
|
|||
deviceReservation.generation);
|
||||
cDevice = deviceReservation.device;
|
||||
|
||||
auto swapChainReservation = wireClient->ReserveSwapChain(cDevice);
|
||||
auto swapChainReservation = wireClient->ReserveSwapChain(cDevice, &swapChainDesc);
|
||||
wireServer->InjectSwapChain(backendSwapChain, swapChainReservation.id,
|
||||
swapChainReservation.generation, deviceReservation.id,
|
||||
deviceReservation.generation);
|
||||
|
|
|
@ -356,6 +356,10 @@ TEST_P(SwapChainWithAdditionalUsageTests, GetSurfaceSupportedUsage) {
|
|||
|
||||
// Test that sampling from swapchain is supported.
|
||||
TEST_P(SwapChainWithAdditionalUsageTests, SamplingFromSwapChain) {
|
||||
// TODO(dawn:1551): Reenable on D3D11 after suppressing the D3D11 debug layer warning for
|
||||
// setting the same private data multiple times.
|
||||
DAWN_SUPPRESS_TEST_IF(IsD3D11());
|
||||
|
||||
// Skip all tests if readable surface doesn't support texture binding
|
||||
DAWN_TEST_UNSUPPORTED_IF(
|
||||
(device.GetSupportedSurfaceUsage(surface) & wgpu::TextureUsage::TextureBinding) == 0);
|
||||
|
|
|
@ -70,30 +70,34 @@ class SwapChainValidationTests : public DawnTest {
|
|||
wgpu::SwapChainDescriptor badDescriptor;
|
||||
|
||||
// Checks that a RenderAttachment view is an error by trying to create a render pass on it.
|
||||
void CheckTextureViewIsError(wgpu::TextureView view) { CheckTextureView(view, true, false); }
|
||||
void CheckTextureIsError(wgpu::Texture texture) { CheckTexture(texture, true, false); }
|
||||
|
||||
// Checks that a RenderAttachment view is an error by trying to submit a render pass on it.
|
||||
void CheckTextureViewIsDestroyed(wgpu::TextureView view) {
|
||||
CheckTextureView(view, false, true);
|
||||
}
|
||||
void CheckTextureIsDestroyed(wgpu::Texture texture) { CheckTexture(texture, false, true); }
|
||||
|
||||
// Checks that a RenderAttachment view is valid by submitting a render pass on it.
|
||||
void CheckTextureViewIsValid(wgpu::TextureView view) { CheckTextureView(view, false, false); }
|
||||
void CheckTextureIsValid(wgpu::Texture texture) { CheckTexture(texture, false, false); }
|
||||
|
||||
private:
|
||||
void CheckTextureView(wgpu::TextureView view, bool errorAtFinish, bool errorAtSubmit) {
|
||||
void CheckTexture(wgpu::Texture texture, bool error, bool destroyed) {
|
||||
wgpu::TextureView view;
|
||||
if (error) {
|
||||
ASSERT_DEVICE_ERROR(view = texture.CreateView());
|
||||
} else {
|
||||
view = texture.CreateView();
|
||||
}
|
||||
utils::ComboRenderPassDescriptor renderPassDesc({view});
|
||||
|
||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
|
||||
pass.End();
|
||||
|
||||
if (errorAtFinish) {
|
||||
if (error) {
|
||||
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||
} else {
|
||||
wgpu::CommandBuffer commands = encoder.Finish();
|
||||
|
||||
if (errorAtSubmit) {
|
||||
if (destroyed) {
|
||||
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
|
||||
} else {
|
||||
queue.Submit(1, &commands);
|
||||
|
@ -102,10 +106,22 @@ class SwapChainValidationTests : public DawnTest {
|
|||
}
|
||||
};
|
||||
|
||||
void CheckTextureMatchesDescriptor(const wgpu::Texture& tex,
|
||||
const wgpu::SwapChainDescriptor& desc) {
|
||||
EXPECT_EQ(desc.width, tex.GetWidth());
|
||||
EXPECT_EQ(desc.height, tex.GetHeight());
|
||||
EXPECT_EQ(desc.usage, tex.GetUsage());
|
||||
EXPECT_EQ(desc.format, tex.GetFormat());
|
||||
EXPECT_EQ(1u, tex.GetDepthOrArrayLayers());
|
||||
EXPECT_EQ(1u, tex.GetMipLevelCount());
|
||||
EXPECT_EQ(1u, tex.GetSampleCount());
|
||||
EXPECT_EQ(wgpu::TextureDimension::e2D, tex.GetDimension());
|
||||
}
|
||||
|
||||
// Control case for a successful swapchain creation and presenting.
|
||||
TEST_P(SwapChainValidationTests, CreationSuccess) {
|
||||
wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
wgpu::TextureView view = swapchain.GetCurrentTextureView();
|
||||
swapchain.GetCurrentTexture();
|
||||
swapchain.Present();
|
||||
}
|
||||
|
||||
|
@ -174,51 +190,51 @@ TEST_P(SwapChainValidationTests, OperationsOnErrorSwapChain) {
|
|||
wgpu::SwapChain swapchain;
|
||||
ASSERT_DEVICE_ERROR(swapchain = device.CreateSwapChain(surface, &badDescriptor));
|
||||
|
||||
wgpu::TextureView view;
|
||||
ASSERT_DEVICE_ERROR(view = swapchain.GetCurrentTextureView());
|
||||
CheckTextureViewIsError(view);
|
||||
wgpu::Texture texture;
|
||||
ASSERT_DEVICE_ERROR(texture = swapchain.GetCurrentTexture());
|
||||
CheckTextureIsError(texture);
|
||||
|
||||
ASSERT_DEVICE_ERROR(swapchain.Present());
|
||||
}
|
||||
|
||||
// Check it is invalid to call present without getting a current view.
|
||||
TEST_P(SwapChainValidationTests, PresentWithoutCurrentView) {
|
||||
// Check it is invalid to call present without getting a current texture.
|
||||
TEST_P(SwapChainValidationTests, PresentWithoutCurrentTexture) {
|
||||
wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
|
||||
// Check it is invalid if we never called GetCurrentTextureView
|
||||
// Check it is invalid if we never called GetCurrentTexture
|
||||
ASSERT_DEVICE_ERROR(swapchain.Present());
|
||||
|
||||
// Check it is invalid if we never called since the last present.
|
||||
swapchain.GetCurrentTextureView();
|
||||
swapchain.GetCurrentTexture();
|
||||
swapchain.Present();
|
||||
ASSERT_DEVICE_ERROR(swapchain.Present());
|
||||
}
|
||||
|
||||
// Check that the current view isn't destroyed when the ref to the swapchain is lost because the
|
||||
// Check that the current texture isn't destroyed when the ref to the swapchain is lost because the
|
||||
// swapchain is kept alive by the surface. Also check after we lose all refs to the surface, the
|
||||
// texture is destroyed.
|
||||
TEST_P(SwapChainValidationTests, ViewValidAfterSwapChainRefLost) {
|
||||
TEST_P(SwapChainValidationTests, TextureValidAfterSwapChainRefLost) {
|
||||
wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
wgpu::TextureView view = swapchain.GetCurrentTextureView();
|
||||
wgpu::Texture texture = swapchain.GetCurrentTexture();
|
||||
|
||||
swapchain = nullptr;
|
||||
CheckTextureViewIsValid(view);
|
||||
CheckTextureIsValid(texture);
|
||||
|
||||
surface = nullptr;
|
||||
CheckTextureViewIsDestroyed(view);
|
||||
CheckTextureIsDestroyed(texture);
|
||||
}
|
||||
|
||||
// Check that the current view is the destroyed state after present.
|
||||
TEST_P(SwapChainValidationTests, ViewDestroyedAfterPresent) {
|
||||
// Check that the current texture is the destroyed state after present.
|
||||
TEST_P(SwapChainValidationTests, TextureDestroyedAfterPresent) {
|
||||
wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
wgpu::TextureView view = swapchain.GetCurrentTextureView();
|
||||
wgpu::Texture texture = swapchain.GetCurrentTexture();
|
||||
swapchain.Present();
|
||||
|
||||
CheckTextureViewIsDestroyed(view);
|
||||
CheckTextureIsDestroyed(texture);
|
||||
}
|
||||
|
||||
// Check that returned view is of the current format / usage / dimension / size / sample count
|
||||
TEST_P(SwapChainValidationTests, ReturnedViewCharacteristics) {
|
||||
// Check that returned texture is of the current format / usage / dimension / size / sample count
|
||||
TEST_P(SwapChainValidationTests, ReturnedTextureCharacteristics) {
|
||||
utils::ComboRenderPipelineDescriptor pipelineDesc;
|
||||
pipelineDesc.vertex.module = utils::CreateShaderModule(device, R"(
|
||||
@vertex fn main() -> @builtin(position) vec4f {
|
||||
|
@ -255,7 +271,7 @@ TEST_P(SwapChainValidationTests, ReturnedViewCharacteristics) {
|
|||
|
||||
// Get the swapchain view and try to use it in the render pass to trigger all the validation.
|
||||
wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
wgpu::TextureView view = swapchain.GetCurrentTextureView();
|
||||
wgpu::TextureView view = swapchain.GetCurrentTexture().CreateView();
|
||||
|
||||
// Validation will also check the dimension of the view is 2D, and it's usage contains
|
||||
// RenderAttachment
|
||||
|
@ -267,11 +283,42 @@ TEST_P(SwapChainValidationTests, ReturnedViewCharacteristics) {
|
|||
|
||||
queue.Submit(1, &commands);
|
||||
|
||||
// Check that view doesn't have extra formats like Sampled.
|
||||
// TODO(cwallez@chromium.org): also check for [Readonly]Storage once that's implemented.
|
||||
// Check that view doesn't have an extra Sampled usage.
|
||||
wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
|
||||
device, {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float}});
|
||||
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, bgl, {{0, view}}));
|
||||
|
||||
// Check that view doesn't have an extra Storage usage.
|
||||
bgl = utils::MakeBindGroupLayout(
|
||||
device, {{0, wgpu::ShaderStage::Fragment, wgpu::StorageTextureAccess::WriteOnly,
|
||||
wgpu::TextureFormat::R32Uint}});
|
||||
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, bgl, {{0, view}}));
|
||||
}
|
||||
|
||||
// Check the reflection of textures returned by GetCurrentTexture on valid swapchain.
|
||||
TEST_P(SwapChainValidationTests, ReflectionValidGetCurrentTexture) {
|
||||
// Check with the goodDescriptor.
|
||||
{
|
||||
wgpu::SwapChain swapChain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
CheckTextureMatchesDescriptor(swapChain.GetCurrentTexture(), goodDescriptor);
|
||||
}
|
||||
// Check with properties that can be changed while keeping a valid descriptor.
|
||||
{
|
||||
wgpu::SwapChainDescriptor otherDescriptor = goodDescriptor;
|
||||
otherDescriptor.width = 2;
|
||||
otherDescriptor.height = 7;
|
||||
wgpu::SwapChain swapChain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
CheckTextureMatchesDescriptor(swapChain.GetCurrentTexture(), goodDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
// Check the reflection of textures returned by GetCurrentTexture on valid swapchain.
|
||||
TEST_P(SwapChainValidationTests, ReflectionErrorGetCurrentTexture) {
|
||||
wgpu::SwapChain swapChain;
|
||||
ASSERT_DEVICE_ERROR(swapChain = device.CreateSwapChain(surface, &badDescriptor));
|
||||
wgpu::Texture texture;
|
||||
ASSERT_DEVICE_ERROR(texture = swapChain.GetCurrentTexture());
|
||||
CheckTextureMatchesDescriptor(texture, badDescriptor);
|
||||
}
|
||||
|
||||
// Check that failing to create a new swapchain doesn't replace the previous one.
|
||||
|
@ -279,59 +326,59 @@ TEST_P(SwapChainValidationTests, ErrorSwapChainDoesntReplacePreviousOne) {
|
|||
wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
ASSERT_DEVICE_ERROR(device.CreateSwapChain(surface, &badDescriptor));
|
||||
|
||||
wgpu::TextureView view = swapchain.GetCurrentTextureView();
|
||||
swapchain.GetCurrentTexture();
|
||||
swapchain.Present();
|
||||
}
|
||||
|
||||
// Check that after replacement, all swapchain operations are errors and the view is destroyed.
|
||||
// Check that after replacement, all swapchain operations are errors and the texture is destroyed.
|
||||
TEST_P(SwapChainValidationTests, ReplacedSwapChainIsInvalid) {
|
||||
{
|
||||
wgpu::SwapChain replacedSwapChain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
device.CreateSwapChain(surface, &goodDescriptor);
|
||||
ASSERT_DEVICE_ERROR(replacedSwapChain.GetCurrentTextureView());
|
||||
ASSERT_DEVICE_ERROR(replacedSwapChain.GetCurrentTexture());
|
||||
}
|
||||
|
||||
{
|
||||
wgpu::SwapChain replacedSwapChain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
wgpu::TextureView view = replacedSwapChain.GetCurrentTextureView();
|
||||
wgpu::Texture texture = replacedSwapChain.GetCurrentTexture();
|
||||
device.CreateSwapChain(surface, &goodDescriptor);
|
||||
|
||||
CheckTextureViewIsDestroyed(view);
|
||||
CheckTextureIsDestroyed(texture);
|
||||
ASSERT_DEVICE_ERROR(replacedSwapChain.Present());
|
||||
}
|
||||
}
|
||||
|
||||
// Check that after surface destruction, all swapchain operations are errors and the view is
|
||||
// Check that after surface destruction, all swapchain operations are errors and the texture is
|
||||
// destroyed. The test is split in two to reset the wgpu::Surface in the middle.
|
||||
TEST_P(SwapChainValidationTests, SwapChainIsInvalidAfterSurfaceDestruction_GetView) {
|
||||
TEST_P(SwapChainValidationTests, SwapChainIsInvalidAfterSurfaceDestruction_GetTexture) {
|
||||
wgpu::SwapChain replacedSwapChain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
surface = nullptr;
|
||||
ASSERT_DEVICE_ERROR(replacedSwapChain.GetCurrentTextureView());
|
||||
ASSERT_DEVICE_ERROR(replacedSwapChain.GetCurrentTexture());
|
||||
}
|
||||
TEST_P(SwapChainValidationTests, SwapChainIsInvalidAfterSurfaceDestruction_AfterGetView) {
|
||||
TEST_P(SwapChainValidationTests, SwapChainIsInvalidAfterSurfaceDestruction_AfterGetTexture) {
|
||||
wgpu::SwapChain replacedSwapChain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
wgpu::TextureView view = replacedSwapChain.GetCurrentTextureView();
|
||||
wgpu::Texture texture = replacedSwapChain.GetCurrentTexture();
|
||||
surface = nullptr;
|
||||
|
||||
CheckTextureViewIsDestroyed(view);
|
||||
CheckTextureIsDestroyed(texture);
|
||||
ASSERT_DEVICE_ERROR(replacedSwapChain.Present());
|
||||
}
|
||||
|
||||
// Test that new swap chain present fails after device is lost
|
||||
TEST_P(SwapChainValidationTests, SwapChainPresentFailsAfterDeviceLost) {
|
||||
wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
wgpu::TextureView view = swapchain.GetCurrentTextureView();
|
||||
swapchain.GetCurrentTexture();
|
||||
|
||||
LoseDeviceForTesting();
|
||||
ASSERT_DEVICE_ERROR(swapchain.Present());
|
||||
}
|
||||
|
||||
// Test that new swap chain get current texture view fails after device is lost
|
||||
TEST_P(SwapChainValidationTests, SwapChainGetCurrentTextureViewFailsAfterDevLost) {
|
||||
// Test that new swap chain get current texture fails after device is lost
|
||||
TEST_P(SwapChainValidationTests, SwapChainGetCurrentTextureFailsAfterDevLost) {
|
||||
wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor);
|
||||
|
||||
LoseDeviceForTesting();
|
||||
ASSERT_DEVICE_ERROR(swapchain.GetCurrentTextureView());
|
||||
ASSERT_DEVICE_ERROR(swapchain.GetCurrentTexture());
|
||||
}
|
||||
|
||||
// Test that creation of a new swapchain fails after device is lost
|
||||
|
|
|
@ -21,17 +21,27 @@ namespace dawn::wire {
|
|||
namespace {
|
||||
|
||||
using testing::Mock;
|
||||
using testing::Return;
|
||||
|
||||
class WireInjectSwapChainTests : public WireTest {
|
||||
public:
|
||||
WireInjectSwapChainTests() {}
|
||||
WireInjectSwapChainTests() {
|
||||
swapChainDesc = {};
|
||||
swapChainDesc.usage = WGPUTextureUsage_RenderAttachment;
|
||||
swapChainDesc.format = WGPUTextureFormat_RGBA8Unorm;
|
||||
swapChainDesc.width = 17;
|
||||
swapChainDesc.height = 42;
|
||||
swapChainDesc.presentMode = WGPUPresentMode_Mailbox;
|
||||
}
|
||||
~WireInjectSwapChainTests() override = default;
|
||||
|
||||
WGPUSwapChainDescriptor swapChainDesc;
|
||||
};
|
||||
|
||||
// Test that reserving and injecting a swapchain makes calls on the client object forward to the
|
||||
// server object correctly.
|
||||
TEST_F(WireInjectSwapChainTests, CallAfterReserveInject) {
|
||||
ReservedSwapChain reservation = GetWireClient()->ReserveSwapChain(device);
|
||||
ReservedSwapChain reservation = GetWireClient()->ReserveSwapChain(device, &swapChainDesc);
|
||||
|
||||
WGPUSwapChain apiSwapchain = api.GetNewSwapChain();
|
||||
EXPECT_CALL(api, SwapChainReference(apiSwapchain));
|
||||
|
@ -46,8 +56,8 @@ TEST_F(WireInjectSwapChainTests, CallAfterReserveInject) {
|
|||
|
||||
// Test that reserve correctly returns different IDs each time.
|
||||
TEST_F(WireInjectSwapChainTests, ReserveDifferentIDs) {
|
||||
ReservedSwapChain reservation1 = GetWireClient()->ReserveSwapChain(device);
|
||||
ReservedSwapChain reservation2 = GetWireClient()->ReserveSwapChain(device);
|
||||
ReservedSwapChain reservation1 = GetWireClient()->ReserveSwapChain(device, &swapChainDesc);
|
||||
ReservedSwapChain reservation2 = GetWireClient()->ReserveSwapChain(device, &swapChainDesc);
|
||||
|
||||
ASSERT_NE(reservation1.id, reservation2.id);
|
||||
ASSERT_NE(reservation1.swapchain, reservation2.swapchain);
|
||||
|
@ -55,7 +65,7 @@ TEST_F(WireInjectSwapChainTests, ReserveDifferentIDs) {
|
|||
|
||||
// Test that injecting the same id without a destroy first fails.
|
||||
TEST_F(WireInjectSwapChainTests, InjectExistingID) {
|
||||
ReservedSwapChain reservation = GetWireClient()->ReserveSwapChain(device);
|
||||
ReservedSwapChain reservation = GetWireClient()->ReserveSwapChain(device, &swapChainDesc);
|
||||
|
||||
WGPUSwapChain apiSwapchain = api.GetNewSwapChain();
|
||||
EXPECT_CALL(api, SwapChainReference(apiSwapchain));
|
||||
|
@ -71,7 +81,7 @@ TEST_F(WireInjectSwapChainTests, InjectExistingID) {
|
|||
|
||||
// Test that the server only borrows the swapchain and does a single reference-release
|
||||
TEST_F(WireInjectSwapChainTests, InjectedSwapChainLifetime) {
|
||||
ReservedSwapChain reservation = GetWireClient()->ReserveSwapChain(device);
|
||||
ReservedSwapChain reservation = GetWireClient()->ReserveSwapChain(device, &swapChainDesc);
|
||||
|
||||
// Injecting the swapchain adds a reference
|
||||
WGPUSwapChain apiSwapchain = api.GetNewSwapChain();
|
||||
|
@ -95,17 +105,17 @@ TEST_F(WireInjectSwapChainTests, InjectedSwapChainLifetime) {
|
|||
TEST_F(WireInjectSwapChainTests, ReclaimSwapChainReservation) {
|
||||
// Test that doing a reservation and full release is an error.
|
||||
{
|
||||
ReservedSwapChain reservation = GetWireClient()->ReserveSwapChain(device);
|
||||
ReservedSwapChain reservation = GetWireClient()->ReserveSwapChain(device, &swapChainDesc);
|
||||
wgpuSwapChainRelease(reservation.swapchain);
|
||||
FlushClient(false);
|
||||
}
|
||||
|
||||
// Test that doing a reservation and then reclaiming it recycles the ID.
|
||||
{
|
||||
ReservedSwapChain reservation1 = GetWireClient()->ReserveSwapChain(device);
|
||||
ReservedSwapChain reservation1 = GetWireClient()->ReserveSwapChain(device, &swapChainDesc);
|
||||
GetWireClient()->ReclaimSwapChainReservation(reservation1);
|
||||
|
||||
ReservedSwapChain reservation2 = GetWireClient()->ReserveSwapChain(device);
|
||||
ReservedSwapChain reservation2 = GetWireClient()->ReserveSwapChain(device, &swapChainDesc);
|
||||
|
||||
// The ID is the same, but the generation is still different.
|
||||
ASSERT_EQ(reservation1.id, reservation2.id);
|
||||
|
@ -116,5 +126,30 @@ TEST_F(WireInjectSwapChainTests, ReclaimSwapChainReservation) {
|
|||
}
|
||||
}
|
||||
|
||||
// Test that the texture's reflection is correct for injected swapchains in the wire.
|
||||
TEST_F(WireInjectSwapChainTests, SwapChainTextureReflection) {
|
||||
ReservedSwapChain reservation = GetWireClient()->ReserveSwapChain(device, &swapChainDesc);
|
||||
|
||||
WGPUSwapChain apiSwapchain = api.GetNewSwapChain();
|
||||
EXPECT_CALL(api, SwapChainReference(apiSwapchain));
|
||||
ASSERT_TRUE(GetWireServer()->InjectSwapChain(apiSwapchain, reservation.id,
|
||||
reservation.generation, reservation.deviceId,
|
||||
reservation.deviceGeneration));
|
||||
|
||||
WGPUTexture tex = wgpuSwapChainGetCurrentTexture(reservation.swapchain);
|
||||
WGPUTexture apiTex = api.GetNewTexture();
|
||||
EXPECT_CALL(api, SwapChainGetCurrentTexture(apiSwapchain)).WillOnce(Return(apiTex));
|
||||
FlushClient();
|
||||
|
||||
EXPECT_EQ(swapChainDesc.width, wgpuTextureGetWidth(tex));
|
||||
EXPECT_EQ(swapChainDesc.height, wgpuTextureGetHeight(tex));
|
||||
EXPECT_EQ(swapChainDesc.usage, wgpuTextureGetUsage(tex));
|
||||
EXPECT_EQ(swapChainDesc.format, wgpuTextureGetFormat(tex));
|
||||
EXPECT_EQ(1u, wgpuTextureGetDepthOrArrayLayers(tex));
|
||||
EXPECT_EQ(1u, wgpuTextureGetMipLevelCount(tex));
|
||||
EXPECT_EQ(1u, wgpuTextureGetSampleCount(tex));
|
||||
EXPECT_EQ(WGPUTextureDimension_2D, wgpuTextureGetDimension(tex));
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
} // namespace dawn::wire
|
||||
|
|
|
@ -101,6 +101,8 @@ dawn_component("wire") {
|
|||
"client/RequestTracker.h",
|
||||
"client/ShaderModule.cpp",
|
||||
"client/ShaderModule.h",
|
||||
"client/SwapChain.cpp",
|
||||
"client/SwapChain.h",
|
||||
"client/Texture.cpp",
|
||||
"client/Texture.h",
|
||||
"server/ObjectStorage.h",
|
||||
|
|
|
@ -74,6 +74,8 @@ target_sources(dawn_wire PRIVATE
|
|||
"client/RequestTracker.h"
|
||||
"client/ShaderModule.cpp"
|
||||
"client/ShaderModule.h"
|
||||
"client/SwapChain.cpp"
|
||||
"client/SwapChain.h"
|
||||
"client/Texture.cpp"
|
||||
"client/Texture.h"
|
||||
"server/ObjectStorage.h"
|
||||
|
|
|
@ -33,8 +33,9 @@ ReservedTexture WireClient::ReserveTexture(WGPUDevice device,
|
|||
return mImpl->ReserveTexture(device, descriptor);
|
||||
}
|
||||
|
||||
ReservedSwapChain WireClient::ReserveSwapChain(WGPUDevice device) {
|
||||
return mImpl->ReserveSwapChain(device);
|
||||
ReservedSwapChain WireClient::ReserveSwapChain(WGPUDevice device,
|
||||
const WGPUSwapChainDescriptor* descriptor) {
|
||||
return mImpl->ReserveSwapChain(device, descriptor);
|
||||
}
|
||||
|
||||
ReservedDevice WireClient::ReserveDevice() {
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "dawn/wire/client/QuerySet.h"
|
||||
#include "dawn/wire/client/Queue.h"
|
||||
#include "dawn/wire/client/ShaderModule.h"
|
||||
#include "dawn/wire/client/SwapChain.h"
|
||||
#include "dawn/wire/client/Texture.h"
|
||||
|
||||
#include "dawn/wire/client/ApiObjects_autogen.h"
|
||||
|
|
|
@ -93,8 +93,9 @@ ReservedTexture Client::ReserveTexture(WGPUDevice device, const WGPUTextureDescr
|
|||
return result;
|
||||
}
|
||||
|
||||
ReservedSwapChain Client::ReserveSwapChain(WGPUDevice device) {
|
||||
SwapChain* swapChain = Make<SwapChain>();
|
||||
ReservedSwapChain Client::ReserveSwapChain(WGPUDevice device,
|
||||
const WGPUSwapChainDescriptor* descriptor) {
|
||||
SwapChain* swapChain = Make<SwapChain>(nullptr, descriptor);
|
||||
|
||||
ReservedSwapChain result;
|
||||
result.swapchain = ToAPI(swapChain);
|
||||
|
|
|
@ -71,7 +71,8 @@ class Client : public ClientBase {
|
|||
MemoryTransferService* GetMemoryTransferService() const { return mMemoryTransferService; }
|
||||
|
||||
ReservedTexture ReserveTexture(WGPUDevice device, const WGPUTextureDescriptor* descriptor);
|
||||
ReservedSwapChain ReserveSwapChain(WGPUDevice device);
|
||||
ReservedSwapChain ReserveSwapChain(WGPUDevice device,
|
||||
const WGPUSwapChainDescriptor* descriptor);
|
||||
ReservedDevice ReserveDevice();
|
||||
ReservedInstance ReserveInstance();
|
||||
|
||||
|
|
|
@ -213,6 +213,11 @@ WGPUQuerySet Device::CreateQuerySet(const WGPUQuerySetDescriptor* descriptor) {
|
|||
return QuerySet::Create(this, descriptor);
|
||||
}
|
||||
|
||||
WGPUSwapChain Device::CreateSwapChain(WGPUSurface surface,
|
||||
const WGPUSwapChainDescriptor* descriptor) {
|
||||
return SwapChain::Create(this, surface, descriptor);
|
||||
}
|
||||
|
||||
WGPUTexture Device::CreateTexture(const WGPUTextureDescriptor* descriptor) {
|
||||
return Texture::Create(this, descriptor);
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ class Device final : public ObjectBase {
|
|||
WGPUCreateRenderPipelineAsyncCallback callback,
|
||||
void* userdata);
|
||||
WGPUQuerySet CreateQuerySet(const WGPUQuerySetDescriptor* descriptor);
|
||||
WGPUSwapChain CreateSwapChain(WGPUSurface surface, const WGPUSwapChainDescriptor* descriptor);
|
||||
WGPUTexture CreateTexture(const WGPUTextureDescriptor* descriptor);
|
||||
WGPUTexture CreateErrorTexture(const WGPUTextureDescriptor* descriptor);
|
||||
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
// Copyright 2023 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 "dawn/wire/client/SwapChain.h"
|
||||
|
||||
#include "dawn/wire/client/Client.h"
|
||||
#include "dawn/wire/client/Device.h"
|
||||
#include "dawn/wire/client/Texture.h"
|
||||
|
||||
namespace dawn::wire::client {
|
||||
|
||||
// static
|
||||
WGPUSwapChain SwapChain::Create(Device* device,
|
||||
WGPUSurface surface,
|
||||
const WGPUSwapChainDescriptor* descriptor) {
|
||||
Client* wireClient = device->GetClient();
|
||||
SwapChain* swapChain = wireClient->Make<SwapChain>(surface, descriptor);
|
||||
|
||||
// Send the Device::CreateSwapChain command without modifications.
|
||||
DeviceCreateSwapChainCmd cmd;
|
||||
cmd.self = ToAPI(device);
|
||||
cmd.selfId = device->GetWireId();
|
||||
cmd.descriptor = descriptor;
|
||||
cmd.result = swapChain->GetWireHandle();
|
||||
wireClient->SerializeCommand(cmd);
|
||||
|
||||
return ToAPI(swapChain);
|
||||
}
|
||||
|
||||
SwapChain::SwapChain(const ObjectBaseParams& params,
|
||||
WGPUSurface,
|
||||
const WGPUSwapChainDescriptor* descriptor)
|
||||
: ObjectBase(params) {
|
||||
mTextureDescriptor = {};
|
||||
mTextureDescriptor.size = {descriptor->width, descriptor->height, 1};
|
||||
mTextureDescriptor.format = descriptor->format;
|
||||
mTextureDescriptor.usage = descriptor->usage;
|
||||
mTextureDescriptor.dimension = WGPUTextureDimension_2D;
|
||||
mTextureDescriptor.mipLevelCount = 1;
|
||||
mTextureDescriptor.sampleCount = 1;
|
||||
}
|
||||
|
||||
SwapChain::~SwapChain() = default;
|
||||
|
||||
WGPUTexture SwapChain::GetCurrentTexture() {
|
||||
Client* wireClient = GetClient();
|
||||
Texture* texture = wireClient->Make<Texture>(&mTextureDescriptor);
|
||||
|
||||
SwapChainGetCurrentTextureCmd cmd;
|
||||
cmd.self = ToAPI(this);
|
||||
cmd.selfId = GetWireId();
|
||||
cmd.result = texture->GetWireHandle();
|
||||
wireClient->SerializeCommand(cmd);
|
||||
|
||||
return ToAPI(texture);
|
||||
}
|
||||
|
||||
} // namespace dawn::wire::client
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright 2023 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.
|
||||
|
||||
#ifndef SRC_DAWN_WIRE_CLIENT_SWAPCHAIN_H_
|
||||
#define SRC_DAWN_WIRE_CLIENT_SWAPCHAIN_H_
|
||||
|
||||
#include "dawn/webgpu.h"
|
||||
|
||||
#include "dawn/wire/client/ObjectBase.h"
|
||||
|
||||
namespace dawn::wire::client {
|
||||
|
||||
class Device;
|
||||
|
||||
class SwapChain final : public ObjectBase {
|
||||
public:
|
||||
static WGPUSwapChain Create(Device* device,
|
||||
WGPUSurface surface,
|
||||
const WGPUSwapChainDescriptor* descriptor);
|
||||
|
||||
SwapChain(const ObjectBaseParams& params,
|
||||
WGPUSurface surface,
|
||||
const WGPUSwapChainDescriptor* descriptor);
|
||||
~SwapChain() override;
|
||||
|
||||
WGPUTexture GetCurrentTexture();
|
||||
|
||||
private:
|
||||
WGPUTextureDescriptor mTextureDescriptor;
|
||||
};
|
||||
|
||||
} // namespace dawn::wire::client
|
||||
|
||||
#endif // SRC_DAWN_WIRE_CLIENT_SWAPCHAIN_H_
|
Loading…
Reference in New Issue