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:
Corentin Wallez 2023-05-26 10:32:17 +00:00 committed by Dawn LUCI CQ
parent 2ef42221d4
commit 943a1a2d7a
22 changed files with 328 additions and 73 deletions

View File

@ -2576,6 +2576,7 @@
"category": "object",
"methods": [
{"name": "get current texture view", "returns": "texture view"},
{"name": "get current texture", "returns": "texture"},
{"name": "present"}
]
},

View File

@ -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": [

View File

@ -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();

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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",

View File

@ -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"

View File

@ -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() {

View File

@ -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"

View File

@ -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);

View File

@ -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();

View File

@ -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);
}

View File

@ -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);

View File

@ -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

View File

@ -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_