diff --git a/dawn.json b/dawn.json index 859df44b4d..91107cacbb 100644 --- a/dawn.json +++ b/dawn.json @@ -20,6 +20,7 @@ "adapter properties": { "category": "structure", "extensible": true, + "output": true, "members": [ {"name": "device ID", "type": "uint32_t"}, {"name": "vendor ID", "type": "uint32_t"}, @@ -734,6 +735,13 @@ {"name": "userdata", "type": "void", "annotation": "*"} ] }, + { + "name": "create external texture", + "returns": "external texture", + "args": [ + {"name": "external texture descriptor", "type": "external texture descriptor", "annotation": "const*"} + ] + }, { "name": "create pipeline layout", "returns": "pipeline layout", @@ -801,15 +809,15 @@ ] }, { - "name": "get queue", - "returns": "queue" + "name": "get limits", + "returns": "bool", + "args": [ + {"name": "limits", "type": "supported limits", "annotation": "*"} + ] }, { - "name": "create external texture", - "returns": "external texture", - "args": [ - {"name": "external texture descriptor", "type": "external texture descriptor", "annotation": "const*"} - ] + "name": "get queue", + "returns": "queue" }, { "name": "inject error", @@ -885,7 +893,7 @@ {"name": "depth clamping", "type": "bool", "default": "false"}, {"name": "invalid extension", "type": "bool", "default": "false"}, {"name": "dawn internal usages", "type": "bool", "default": "false"}, - {"name": "limits", "type": "limits"} + {"name": "limits", "type": "supported limits"} ] }, "double": { @@ -901,7 +909,6 @@ }, "limits": { "category": "structure", - "extensible": true, "members": [ {"name": "max texture dimension 1D", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"}, {"name": "max texture dimension 2D", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"}, @@ -931,6 +938,21 @@ {"name": "max compute workgroups per dimension", "type": "uint32_t", "default": "WGPU_LIMIT_U32_UNDEFINED"} ] }, + "required limits": { + "category": "structure", + "extensible": true, + "members": [ + {"name": "limits", "type": "limits"} + ] + }, + "supported limits": { + "category": "structure", + "extensible": true, + "output": true, + "members": [ + {"name": "limits", "type": "limits"} + ] + }, "logging callback": { "category": "callback", "args": [ diff --git a/dawn_wire.json b/dawn_wire.json index 5f881c1e90..7d4b89c397 100644 --- a/dawn_wire.json +++ b/dawn_wire.json @@ -153,6 +153,7 @@ "DeviceCreateBuffer", "DeviceCreateComputePipelineAsync", "DeviceCreateRenderPipelineAsync", + "DeviceGetLimits", "DevicePopErrorScope", "DeviceSetDeviceLostCallback", "DeviceSetUncapturedErrorCallback", diff --git a/generator/dawn_json_generator.py b/generator/dawn_json_generator.py index c6268ed714..23558e60d5 100644 --- a/generator/dawn_json_generator.py +++ b/generator/dawn_json_generator.py @@ -206,6 +206,7 @@ class StructureType(Record, Type): Type.__init__(self, name, json_data) self.chained = json_data.get("chained", False) self.extensible = json_data.get("extensible", False) + self.output = json_data.get("output", False) # Chained structs inherit from wgpu::ChainedStruct, which has # nextInChain, so setting both extensible and chained would result in # two nextInChain members. diff --git a/generator/templates/dawn_wire/WireCmd.cpp b/generator/templates/dawn_wire/WireCmd.cpp index 41809a1d0f..c9dc5e2ceb 100644 --- a/generator/templates/dawn_wire/WireCmd.cpp +++ b/generator/templates/dawn_wire/WireCmd.cpp @@ -496,13 +496,22 @@ namespace dawn_wire { } size_t GetChainedStructExtraRequiredSize(const WGPUChainedStruct* chainedStruct); - DAWN_NO_DISCARD WireResult SerializeChainedStruct(WGPUChainedStruct const* chainedStruct, + DAWN_NO_DISCARD WireResult SerializeChainedStruct(const WGPUChainedStruct* chainedStruct, SerializeBuffer* buffer, const ObjectIdProvider& provider); WireResult DeserializeChainedStruct(const WGPUChainedStruct** outChainNext, - DeserializeBuffer* deserializeBuffer, - DeserializeAllocator* allocator, - const ObjectIdResolver& resolver); + DeserializeBuffer* deserializeBuffer, + DeserializeAllocator* allocator, + const ObjectIdResolver& resolver); + + size_t GetChainedStructExtraRequiredSize(WGPUChainedStructOut* chainedStruct); + DAWN_NO_DISCARD WireResult SerializeChainedStruct(WGPUChainedStructOut* chainedStruct, + SerializeBuffer* buffer, + const ObjectIdProvider& provider); + WireResult DeserializeChainedStruct(WGPUChainedStructOut** outChainNext, + DeserializeBuffer* deserializeBuffer, + DeserializeAllocator* allocator, + const ObjectIdResolver& resolver); //* Output structure [de]serialization first because it is used by commands. {% for type in by_category["structure"] %} @@ -513,12 +522,19 @@ namespace dawn_wire { {% endif %} {% endfor %} - size_t GetChainedStructExtraRequiredSize(const WGPUChainedStruct* chainedStruct) { +{% macro make_chained_struct_serialization_helpers(out) %} + {% set ChainedStructPtr = "WGPUChainedStructOut*" if out else "const WGPUChainedStruct*" %} + {% set ChainedStruct = "WGPUChainedStructOut" if out else "WGPUChainedStruct" %} + size_t GetChainedStructExtraRequiredSize({{ChainedStructPtr}} chainedStruct) { ASSERT(chainedStruct != nullptr); size_t result = 0; while (chainedStruct != nullptr) { switch (chainedStruct->sType) { - {% for sType in types["s type"].values if sType.valid and sType.name.CamelCase() not in client_side_structures %} + {% for sType in types["s type"].values if ( + sType.valid and + (sType.name.CamelCase() not in client_side_structures) and + (types[sType.name.get()].output == out) + ) %} case {{as_cEnum(types["s type"].name, sType.name)}}: { const auto& typedStruct = *reinterpret_cast<{{as_cType(sType.name)}} const *>(chainedStruct); result += sizeof({{as_cType(sType.name)}}Transfer); @@ -527,6 +543,8 @@ namespace dawn_wire { break; } {% endfor %} + // Explicitly list the Invalid enum. MSVC complains about no case labels. + case WGPUSType_Invalid: default: // Invalid enum. Reserve space just for the transfer header (sType and hasNext). result += sizeof(WGPUChainedStructTransfer); @@ -537,14 +555,18 @@ namespace dawn_wire { return result; } - DAWN_NO_DISCARD WireResult SerializeChainedStruct(WGPUChainedStruct const* chainedStruct, + DAWN_NO_DISCARD WireResult SerializeChainedStruct({{ChainedStructPtr}} chainedStruct, SerializeBuffer* buffer, const ObjectIdProvider& provider) { ASSERT(chainedStruct != nullptr); ASSERT(buffer != nullptr); do { switch (chainedStruct->sType) { - {% for sType in types["s type"].values if sType.valid and sType.name.CamelCase() not in client_side_structures %} + {% for sType in types["s type"].values if ( + sType.valid and + (sType.name.CamelCase() not in client_side_structures) and + (types[sType.name.get()].output == out) + ) %} {% set CType = as_cType(sType.name) %} case {{as_cEnum(types["s type"].name, sType.name)}}: { @@ -562,6 +584,8 @@ namespace dawn_wire { chainedStruct = chainedStruct->next; } break; {% endfor %} + // Explicitly list the Invalid enum. MSVC complains about no case labels. + case WGPUSType_Invalid: default: { // Invalid enum. Serialize just the transfer header with Invalid as the sType. // TODO(crbug.com/dawn/369): Unknown sTypes are silently discarded. @@ -583,17 +607,21 @@ namespace dawn_wire { return WireResult::Success; } - WireResult DeserializeChainedStruct(const WGPUChainedStruct** outChainNext, - DeserializeBuffer* deserializeBuffer, - DeserializeAllocator* allocator, - const ObjectIdResolver& resolver) { + WireResult DeserializeChainedStruct({{ChainedStructPtr}}* outChainNext, + DeserializeBuffer* deserializeBuffer, + DeserializeAllocator* allocator, + const ObjectIdResolver& resolver) { bool hasNext; do { const volatile WGPUChainedStructTransfer* header; WIRE_TRY(deserializeBuffer->Peek(&header)); WGPUSType sType = header->sType; switch (sType) { - {% for sType in types["s type"].values if sType.valid and sType.name.CamelCase() not in client_side_structures %} + {% for sType in types["s type"].values if ( + sType.valid and + (sType.name.CamelCase() not in client_side_structures) and + (types[sType.name.get()].output == out) + ) %} {% set CType = as_cType(sType.name) %} case {{as_cEnum(types["s type"].name, sType.name)}}: { const volatile {{CType}}Transfer* transfer; @@ -616,6 +644,8 @@ namespace dawn_wire { hasNext = transfer->chain.hasNext; } break; {% endfor %} + // Explicitly list the Invalid enum. MSVC complains about no case labels. + case WGPUSType_Invalid: default: { // Invalid enum. Deserialize just the transfer header with Invalid as the sType. // TODO(crbug.com/dawn/369): Unknown sTypes are silently discarded. @@ -626,8 +656,8 @@ namespace dawn_wire { const volatile WGPUChainedStructTransfer* transfer; WIRE_TRY(deserializeBuffer->Read(&transfer)); - WGPUChainedStruct* outStruct; - WIRE_TRY(GetSpace(allocator, sizeof(WGPUChainedStruct), &outStruct)); + {{ChainedStruct}}* outStruct; + WIRE_TRY(GetSpace(allocator, sizeof({{ChainedStruct}}), &outStruct)); outStruct->sType = WGPUSType_Invalid; outStruct->next = nullptr; @@ -642,6 +672,10 @@ namespace dawn_wire { return WireResult::Success; } +{% endmacro %} + +{{ make_chained_struct_serialization_helpers(False) }} +{{ make_chained_struct_serialization_helpers(True) }} //* Output [de]serialization helpers for commands {% for command in cmd_records["command"] %} @@ -730,4 +764,38 @@ namespace dawn_wire { nullptr, resolver) == WireResult::Success; } + size_t SerializedWGPUSupportedLimitsSize(const WGPUSupportedLimits* supportedLimits) { + return sizeof(WGPUSupportedLimits) + + WGPUSupportedLimitsGetExtraRequiredSize(*supportedLimits); + } + + void SerializeWGPUSupportedLimits( + const WGPUSupportedLimits* supportedLimits, + char* buffer) { + SerializeBuffer serializeBuffer(buffer, SerializedWGPUSupportedLimitsSize(supportedLimits)); + + WGPUSupportedLimitsTransfer* transfer; + + WireResult result = serializeBuffer.Next(&transfer); + ASSERT(result == WireResult::Success); + + ErrorObjectIdProvider provider; + result = WGPUSupportedLimitsSerialize(*supportedLimits, transfer, &serializeBuffer, provider); + ASSERT(result == WireResult::Success); + } + + bool DeserializeWGPUSupportedLimits(WGPUSupportedLimits* supportedLimits, + const volatile char* buffer, + size_t size) { + const volatile WGPUSupportedLimitsTransfer* transfer; + DeserializeBuffer deserializeBuffer(buffer, size); + if (deserializeBuffer.Read(&transfer) != WireResult::Success) { + return false; + } + + ErrorObjectIdResolver resolver; + return WGPUSupportedLimitsDeserialize(supportedLimits, transfer, &deserializeBuffer, + nullptr, resolver) == WireResult::Success; + } + } // namespace dawn_wire diff --git a/generator/templates/webgpu.h b/generator/templates/webgpu.h index 3b91306d7e..e42c7ace27 100644 --- a/generator/templates/webgpu.h +++ b/generator/templates/webgpu.h @@ -106,13 +106,20 @@ typedef struct WGPUChainedStruct { WGPUSType sType; } WGPUChainedStruct; +typedef struct WGPUChainedStructOut { + struct WGPUChainedStructOut * next; + WGPUSType sType; +} WGPUChainedStructOut; + {% for type in by_category["structure"] %} typedef struct {{as_cType(type.name)}} { + {% set Out = "Out" if type.output else "" %} + {% set const = "const" if not type.output else "" %} {% if type.extensible %} - WGPUChainedStruct const * nextInChain; + WGPUChainedStruct{{Out}} {{const}} * nextInChain; {% endif %} {% if type.chained %} - WGPUChainedStruct chain; + WGPUChainedStruct{{Out}} chain; {% endif %} {% for member in type.members %} {{as_annotated_cType(member)}}; diff --git a/generator/templates/webgpu_cpp.h b/generator/templates/webgpu_cpp.h index dd99b11f00..fbb386d689 100644 --- a/generator/templates/webgpu_cpp.h +++ b/generator/templates/webgpu_cpp.h @@ -206,9 +206,16 @@ namespace wgpu { SType sType = SType::Invalid; }; + struct ChainedStructOut { + ChainedStruct * nextInChain = nullptr; + SType sType = SType::Invalid; + }; + {% for type in by_category["structure"] %} + {% set Out = "Out" if type.output else "" %} + {% set const = "const" if not type.output else "" %} {% if type.chained %} - struct {{as_cppType(type.name)}} : ChainedStruct { + struct {{as_cppType(type.name)}} : ChainedStruct{{Out}} { {{as_cppType(type.name)}}() { sType = SType::{{type.name.CamelCase()}}; } @@ -216,13 +223,13 @@ namespace wgpu { struct {{as_cppType(type.name)}} { {% endif %} {% if type.extensible %} - ChainedStruct const * nextInChain = nullptr; + ChainedStruct{{Out}} {{const}} * nextInChain = nullptr; {% endif %} {% for member in type.members %} {% set member_declaration = as_annotated_cppType(member) + render_cpp_default_value(member) %} {% if type.chained and loop.first %} //* Align the first member to ChainedStruct to match the C struct layout. - alignas(ChainedStruct) {{member_declaration}}; + alignas(ChainedStruct{{Out}}) {{member_declaration}}; {% else %} {{member_declaration}}; {% endif %} diff --git a/src/dawn_native/Adapter.cpp b/src/dawn_native/Adapter.cpp index 87b5f9a1fb..5c0d87364c 100644 --- a/src/dawn_native/Adapter.cpp +++ b/src/dawn_native/Adapter.cpp @@ -20,7 +20,6 @@ namespace dawn_native { AdapterBase::AdapterBase(InstanceBase* instance, wgpu::BackendType backend) : mInstance(instance), mBackend(backend) { - mLimits.v1.nextInChain = nullptr; GetDefaultLimits(&mLimits.v1); mSupportedExtensions.EnableExtension(Extension::DawnInternalUsages); } @@ -74,16 +73,16 @@ namespace dawn_native { // to store them (ex. by calling GetLimits directly instead). Currently, // we keep this function as it's only used internally in Chromium to // send the adapter properties across the wire. - GetLimits(reinterpret_cast(&adapterProperties.limits)); + GetLimits(reinterpret_cast(&adapterProperties.limits)); return adapterProperties; } - bool AdapterBase::GetLimits(wgpu::Limits* limits) const { + bool AdapterBase::GetLimits(SupportedLimits* limits) const { ASSERT(limits != nullptr); if (limits->nextInChain != nullptr) { return false; } - *limits = mLimits.v1; + limits->limits = mLimits.v1; return true; } @@ -125,7 +124,8 @@ namespace dawn_native { if (descriptor != nullptr && descriptor->requiredLimits != nullptr) { DAWN_TRY(ValidateLimits( - mLimits.v1, *reinterpret_cast(descriptor->requiredLimits))); + mLimits.v1, + reinterpret_cast(descriptor->requiredLimits)->limits)); if (descriptor->requiredLimits->nextInChain != nullptr) { return DAWN_VALIDATION_ERROR("Unsupported limit extension struct"); diff --git a/src/dawn_native/Adapter.h b/src/dawn_native/Adapter.h index 894a7f8dc3..32cdd719ee 100644 --- a/src/dawn_native/Adapter.h +++ b/src/dawn_native/Adapter.h @@ -52,7 +52,7 @@ namespace dawn_native { const std::vector& requestedExtensions) const; WGPUDeviceProperties GetAdapterProperties() const; - bool GetLimits(wgpu::Limits* limits) const; + bool GetLimits(SupportedLimits* limits) const; virtual bool SupportsExternalImages() const = 0; diff --git a/src/dawn_native/DawnNative.cpp b/src/dawn_native/DawnNative.cpp index 906df18535..8cbda6fe02 100644 --- a/src/dawn_native/DawnNative.cpp +++ b/src/dawn_native/DawnNative.cpp @@ -106,8 +106,8 @@ namespace dawn_native { return mImpl->GetAdapterProperties(); } - bool Adapter::GetLimits(WGPULimits* limits) const { - return mImpl->GetLimits(reinterpret_cast(limits)); + bool Adapter::GetLimits(WGPUSupportedLimits* limits) const { + return mImpl->GetLimits(reinterpret_cast(limits)); } bool Adapter::SupportsExternalImages() const { diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp index 7ade6ab93c..c209cd10b9 100644 --- a/src/dawn_native/Device.cpp +++ b/src/dawn_native/Device.cpp @@ -175,7 +175,7 @@ namespace dawn_native { if (descriptor != nullptr && descriptor->requiredLimits != nullptr) { mLimits.v1 = ReifyDefaultLimits( - *reinterpret_cast(descriptor->requiredLimits)); + reinterpret_cast(descriptor->requiredLimits)->limits); } else { GetDefaultLimits(&mLimits.v1); } @@ -1093,6 +1093,15 @@ namespace dawn_native { } } + bool DeviceBase::APIGetLimits(SupportedLimits* limits) { + ASSERT(limits != nullptr); + if (limits->nextInChain != nullptr) { + return false; + } + limits->limits = mLimits.v1; + return true; + } + void DeviceBase::APIInjectError(wgpu::ErrorType type, const char* message) { if (ConsumedError(ValidateErrorType(type))) { return; diff --git a/src/dawn_native/Device.h b/src/dawn_native/Device.h index 3688ee1905..5ec6f4a158 100644 --- a/src/dawn_native/Device.h +++ b/src/dawn_native/Device.h @@ -209,6 +209,7 @@ namespace dawn_native { QueueBase* APIGetQueue(); + bool APIGetLimits(SupportedLimits* limits); void APIInjectError(wgpu::ErrorType type, const char* message); bool APITick(); diff --git a/src/dawn_native/Limits.cpp b/src/dawn_native/Limits.cpp index 98e067fdd1..33cffdc907 100644 --- a/src/dawn_native/Limits.cpp +++ b/src/dawn_native/Limits.cpp @@ -94,15 +94,15 @@ namespace dawn_native { } // namespace - void GetDefaultLimits(wgpu::Limits* limits) { + void GetDefaultLimits(Limits* limits) { ASSERT(limits != nullptr); #define X(Better, limitName, defaultValue) limits->limitName = defaultValue; LIMITS(X) #undef X } - wgpu::Limits ReifyDefaultLimits(const wgpu::Limits& limits) { - wgpu::Limits out; + Limits ReifyDefaultLimits(const Limits& limits) { + Limits out; #define X(Better, limitName, defaultValue) \ if (!IsLimitUndefined(limits.limitName)) { \ out.limitName = limits.limitName; \ @@ -114,8 +114,7 @@ namespace dawn_native { return out; } - MaybeError ValidateLimits(const wgpu::Limits& supportedLimits, - const wgpu::Limits& requiredLimits) { + MaybeError ValidateLimits(const Limits& supportedLimits, const Limits& requiredLimits) { #define X(Better, limitName, defaultValue) \ if (!IsLimitUndefined(requiredLimits.limitName)) { \ DAWN_TRY(CheckLimit::Invoke(supportedLimits.limitName, \ diff --git a/src/dawn_native/Limits.h b/src/dawn_native/Limits.h index c0358d902a..76f8ec3f55 100644 --- a/src/dawn_native/Limits.h +++ b/src/dawn_native/Limits.h @@ -21,19 +21,18 @@ namespace dawn_native { struct CombinedLimits { - wgpu::Limits v1; + Limits v1; }; // Populate |limits| with the default limits. - void GetDefaultLimits(wgpu::Limits* limits); + void GetDefaultLimits(Limits* limits); // Returns a copy of |limits| where all undefined values are replaced // with their defaults. - wgpu::Limits ReifyDefaultLimits(const wgpu::Limits& limits); + Limits ReifyDefaultLimits(const Limits& limits); // Validate that |requiredLimits| are no better than |supportedLimits|. - MaybeError ValidateLimits(const wgpu::Limits& supportedLimits, - const wgpu::Limits& requiredLimits); + MaybeError ValidateLimits(const Limits& supportedLimits, const Limits& requiredLimits); } // namespace dawn_native diff --git a/src/dawn_wire/client/Device.cpp b/src/dawn_wire/client/Device.cpp index 6ed46988e9..21df71e9f5 100644 --- a/src/dawn_wire/client/Device.cpp +++ b/src/dawn_wire/client/Device.cpp @@ -196,6 +196,12 @@ namespace dawn_wire { namespace client { return Buffer::CreateError(this); } + bool Device::GetLimits(WGPUSupportedLimits* limits) { + // Not implemented in the wire. + UNREACHABLE(); + return false; + } + WGPUQueue Device::GetQueue() { // The queue is lazily created because if a Device is created by // Reserve/Inject, we cannot send the GetQueue message until diff --git a/src/dawn_wire/client/Device.h b/src/dawn_wire/client/Device.h index 849364fdc3..ae2d9fd5e4 100644 --- a/src/dawn_wire/client/Device.h +++ b/src/dawn_wire/client/Device.h @@ -64,6 +64,7 @@ namespace dawn_wire { namespace client { WGPUCreatePipelineAsyncStatus status, const char* message); + bool GetLimits(WGPUSupportedLimits* limits); WGPUQueue GetQueue(); void CancelCallbacksForDisconnect() override; diff --git a/src/include/dawn_native/DawnNative.h b/src/include/dawn_native/DawnNative.h index cb8de7c0ba..b0700301c0 100644 --- a/src/include/dawn_native/DawnNative.h +++ b/src/include/dawn_native/DawnNative.h @@ -67,7 +67,7 @@ namespace dawn_native { std::vector forceEnabledToggles; std::vector forceDisabledToggles; - const WGPULimits* requiredLimits = nullptr; + const WGPURequiredLimits* requiredLimits = nullptr; }; // A struct to record the information of a toggle. A toggle is a code path in Dawn device that @@ -110,7 +110,7 @@ namespace dawn_native { std::vector GetSupportedExtensions() const; WGPUDeviceProperties GetAdapterProperties() const; - bool GetLimits(WGPULimits* limits) const; + bool GetLimits(WGPUSupportedLimits* limits) const; // Check that the Adapter is able to support importing external images. This is necessary // to implement the swapchain and interop APIs in Chromium. diff --git a/src/include/dawn_wire/Wire.h b/src/include/dawn_wire/Wire.h index 4d69c95e42..0c11d91d0c 100644 --- a/src/include/dawn_wire/Wire.h +++ b/src/include/dawn_wire/Wire.h @@ -61,6 +61,16 @@ namespace dawn_wire { const volatile char* deserializeBuffer, size_t deserializeBufferSize); + DAWN_WIRE_EXPORT size_t + SerializedWGPUSupportedLimitsSize(const WGPUSupportedLimits* supportedLimits); + + DAWN_WIRE_EXPORT void SerializeWGPUSupportedLimits(const WGPUSupportedLimits* supportedLimits, + char* serializeBuffer); + + DAWN_WIRE_EXPORT bool DeserializeWGPUSupportedLimits(WGPUSupportedLimits* supportedLimits, + const volatile char* deserializeBuffer, + size_t deserializeBufferSize); + } // namespace dawn_wire #endif // DAWNWIRE_WIRE_H_ diff --git a/src/tests/unittests/validation/RequestDeviceValidationTests.cpp b/src/tests/unittests/validation/RequestDeviceValidationTests.cpp index c4561227ca..5e8bd9b25a 100644 --- a/src/tests/unittests/validation/RequestDeviceValidationTests.cpp +++ b/src/tests/unittests/validation/RequestDeviceValidationTests.cpp @@ -29,6 +29,10 @@ class RequestDeviceValidationTest : public ValidationTest { EXPECT_EQ(status, WGPURequestDeviceStatus_Success); EXPECT_NE(device, nullptr); EXPECT_STREQ(message, nullptr); + if (userdata != nullptr) { + CallCheckDevice(static_cast*>(userdata), + std::move(device)); + } } static void ExpectRequestDeviceError(WGPURequestDeviceStatus status, @@ -40,73 +44,136 @@ class RequestDeviceValidationTest : public ValidationTest { EXPECT_EQ(device, nullptr); EXPECT_STRNE(message, nullptr); } + + template + static void* CheckDevice(F&& f) { + return new std::function(f); + } + + static void CallCheckDevice(std::function* f, wgpu::Device d) { + (*f)(std::move(d)); + delete f; + } }; // Test that requesting a device without specifying limits is valid. TEST_F(RequestDeviceValidationTest, NoRequiredLimits) { dawn_native::DeviceDescriptor descriptor; - adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, nullptr); + adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, + CheckDevice([](wgpu::Device device) { + // Check one of the default limits. + wgpu::SupportedLimits limits; + device.GetLimits(&limits); + EXPECT_EQ(limits.limits.maxBindGroups, 4u); + })); } // Test that requesting a device with the default limits is valid. TEST_F(RequestDeviceValidationTest, DefaultLimits) { - wgpu::Limits limits = {}; + wgpu::RequiredLimits limits = {}; dawn_native::DeviceDescriptor descriptor; - descriptor.requiredLimits = reinterpret_cast(&limits); - adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, nullptr); + descriptor.requiredLimits = reinterpret_cast(&limits); + adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, + CheckDevice([](wgpu::Device device) { + // Check one of the default limits. + wgpu::SupportedLimits limits; + device.GetLimits(&limits); + EXPECT_EQ(limits.limits.maxTextureArrayLayers, 256u); + })); } // Test that requesting a device where a required limit is above the maximum value. TEST_F(RequestDeviceValidationTest, HigherIsBetter) { - wgpu::Limits limits = {}; + wgpu::RequiredLimits limits = {}; dawn_native::DeviceDescriptor descriptor; - descriptor.requiredLimits = reinterpret_cast(&limits); + descriptor.requiredLimits = reinterpret_cast(&limits); - wgpu::Limits supportedLimits; - EXPECT_TRUE(adapter.GetLimits(reinterpret_cast(&supportedLimits))); + wgpu::SupportedLimits supportedLimits; + EXPECT_TRUE(adapter.GetLimits(reinterpret_cast(&supportedLimits))); // Test below the max. - limits.maxBindGroups = supportedLimits.maxBindGroups - 1; - adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, nullptr); + limits.limits.maxBindGroups = supportedLimits.limits.maxBindGroups - 1; + adapter.RequestDevice( + &descriptor, ExpectRequestDeviceSuccess, CheckDevice([&](wgpu::Device device) { + wgpu::SupportedLimits limits; + device.GetLimits(&limits); + + // Check we got exactly the request. + EXPECT_EQ(limits.limits.maxBindGroups, supportedLimits.limits.maxBindGroups - 1); + // Check another default limit. + EXPECT_EQ(limits.limits.maxTextureArrayLayers, 256u); + })); // Test the max. - limits.maxBindGroups = supportedLimits.maxBindGroups; - adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, nullptr); + limits.limits.maxBindGroups = supportedLimits.limits.maxBindGroups; + adapter.RequestDevice( + &descriptor, ExpectRequestDeviceSuccess, CheckDevice([&](wgpu::Device device) { + wgpu::SupportedLimits limits; + device.GetLimits(&limits); + + // Check we got exactly the request. + EXPECT_EQ(limits.limits.maxBindGroups, supportedLimits.limits.maxBindGroups); + // Check another default limit. + EXPECT_EQ(limits.limits.maxTextureArrayLayers, 256u); + })); // Test above the max. - limits.maxBindGroups = supportedLimits.maxBindGroups + 1; + limits.limits.maxBindGroups = supportedLimits.limits.maxBindGroups + 1; adapter.RequestDevice(&descriptor, ExpectRequestDeviceError, nullptr); } // Test that requesting a device where a required limit is below the minimum value. TEST_F(RequestDeviceValidationTest, LowerIsBetter) { - wgpu::Limits limits = {}; + wgpu::RequiredLimits limits = {}; dawn_native::DeviceDescriptor descriptor; - descriptor.requiredLimits = reinterpret_cast(&limits); + descriptor.requiredLimits = reinterpret_cast(&limits); - wgpu::Limits supportedLimits; - EXPECT_TRUE(adapter.GetLimits(reinterpret_cast(&supportedLimits))); + wgpu::SupportedLimits supportedLimits; + EXPECT_TRUE(adapter.GetLimits(reinterpret_cast(&supportedLimits))); // Test below the min. - limits.minUniformBufferOffsetAlignment = supportedLimits.minUniformBufferOffsetAlignment / 2; + limits.limits.minUniformBufferOffsetAlignment = + supportedLimits.limits.minUniformBufferOffsetAlignment / 2; adapter.RequestDevice(&descriptor, ExpectRequestDeviceError, nullptr); // Test the min. - limits.minUniformBufferOffsetAlignment = supportedLimits.minUniformBufferOffsetAlignment; - adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, nullptr); + limits.limits.minUniformBufferOffsetAlignment = + supportedLimits.limits.minUniformBufferOffsetAlignment; + adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, + CheckDevice([&](wgpu::Device device) { + wgpu::SupportedLimits limits; + device.GetLimits(&limits); + + // Check we got exactly the request. + EXPECT_EQ(limits.limits.minUniformBufferOffsetAlignment, + supportedLimits.limits.minUniformBufferOffsetAlignment); + // Check another default limit. + EXPECT_EQ(limits.limits.maxTextureArrayLayers, 256u); + })); // Test above the min. - limits.minUniformBufferOffsetAlignment = supportedLimits.minUniformBufferOffsetAlignment * 2; - adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, nullptr); + limits.limits.minUniformBufferOffsetAlignment = + supportedLimits.limits.minUniformBufferOffsetAlignment * 2; + adapter.RequestDevice(&descriptor, ExpectRequestDeviceSuccess, + CheckDevice([&](wgpu::Device device) { + wgpu::SupportedLimits limits; + device.GetLimits(&limits); + + // Check we got exactly the request. + EXPECT_EQ(limits.limits.minUniformBufferOffsetAlignment, + supportedLimits.limits.minUniformBufferOffsetAlignment * 2); + // Check another default limit. + EXPECT_EQ(limits.limits.maxTextureArrayLayers, 256u); + })); } // Test that it is an error to request limits with an invalid chained struct TEST_F(RequestDeviceValidationTest, InvalidChainedStruct) { wgpu::PrimitiveDepthClampingState depthClamp = {}; - wgpu::Limits limits = {}; + wgpu::RequiredLimits limits = {}; limits.nextInChain = &depthClamp; dawn_native::DeviceDescriptor descriptor; - descriptor.requiredLimits = reinterpret_cast(&limits); + descriptor.requiredLimits = reinterpret_cast(&limits); adapter.RequestDevice(&descriptor, ExpectRequestDeviceError, nullptr); }