GL: implement Feature::TransientAttachments Implement support for TransientAttachment via Renderbuffers. This is a partial reland of https://dawn-review.googlesource.com/c/dawn/+/308775 Renderbuffers are only used for texture formats containing depth or stencil. Bug: 462577182 Change-Id: I92090e92d6e4e1ee1d732ce97d245aae66ea2ef6 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/323915 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Stephen White <senorblanco@chromium.org>
diff --git a/src/dawn/native/opengl/CommandBufferGL.cpp b/src/dawn/native/opengl/CommandBufferGL.cpp index e1f6122..e2a5891 100644 --- a/src/dawn/native/opengl/CommandBufferGL.cpp +++ b/src/dawn/native/opengl/CommandBufferGL.cpp
@@ -415,7 +415,7 @@ }, [&](const TextureBindingInfo&) -> MaybeError { TextureView* view = ToBackend(group->GetBindingAsTextureView(bindingIndex)); - GLuint handle = view->GetHandle(); + GLuint handle = view->GetTextureHandle(); GLenum target = view->GetGLTarget(); FlatBindingIndex viewIndex = indices[bindingIndex]; @@ -477,7 +477,7 @@ [&](const StorageTextureBindingInfo& layout) -> MaybeError { TextureView* view = ToBackend(group->GetBindingAsTextureView(bindingIndex)); Texture* texture = ToBackend(view->GetTexture()); - GLuint handle = texture->GetHandle(); + GLuint handle = texture->GetTextureHandle(); FlatBindingIndex imageIndex = indices[bindingIndex]; GLenum access; @@ -961,7 +961,7 @@ DAWN_TRY(texture->EnsureSubresourceContentInitialized(gl, subresources)); // The only way to move data from a texture to a buffer in GL is via // glReadPixels with a pack buffer. Create a temporary FBO for the copy. - DAWN_GL_TRY(gl, BindTexture(target, texture->GetHandle())); + DAWN_GL_TRY(gl, BindTexture(target, texture->GetTextureHandle())); GLuint readFBO = 0; DAWN_GL_TRY(gl, GenFramebuffers(1, &readFBO)); @@ -1010,9 +1010,9 @@ case wgpu::TextureDimension::e2D: { if (target == GL_TEXTURE_2D) { DAWN_ASSERT(texture->GetArrayLayers() == 1); - DAWN_GL_TRY( - gl, FramebufferTexture2D(GL_READ_FRAMEBUFFER, glAttachment, target, - texture->GetHandle(), src.mipLevel)); + DAWN_GL_TRY(gl, FramebufferTexture2D( + GL_READ_FRAMEBUFFER, glAttachment, target, + texture->GetTextureHandle(), src.mipLevel)); DAWN_GL_TRY(gl, ReadPixels(dchecked_cast<uint32_t>(src.origin.x), dchecked_cast<uint32_t>(src.origin.y), dchecked_cast<uint32_t>(copySize.width), @@ -1026,10 +1026,10 @@ for (TexelCount z{0u}; z < copySize.depthOrArrayLayers; ++z) { GLenum cubeMapTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + dchecked_cast<uint32_t>(z + src.origin.z); - DAWN_GL_TRY( - gl, FramebufferTexture2D(GL_READ_FRAMEBUFFER, glAttachment, - cubeMapTarget, texture->GetHandle(), - src.mipLevel)); + DAWN_GL_TRY(gl, FramebufferTexture2D(GL_READ_FRAMEBUFFER, + glAttachment, cubeMapTarget, + texture->GetTextureHandle(), + src.mipLevel)); DAWN_GL_TRY(gl, ReadPixels(dchecked_cast<uint32_t>(src.origin.x), dchecked_cast<uint32_t>(src.origin.y), dchecked_cast<uint32_t>(copySize.width), @@ -1047,10 +1047,10 @@ const uint64_t bytesPerImage = blockInfo.ToBytes(dst.blocksPerRow * dst.rowsPerImage); for (TexelCount z{0u}; z < copySize.depthOrArrayLayers; ++z) { - DAWN_GL_TRY( - gl, FramebufferTextureLayer( - GL_READ_FRAMEBUFFER, glAttachment, texture->GetHandle(), - src.mipLevel, dchecked_cast<uint32_t>(src.origin.z + z))); + DAWN_GL_TRY(gl, FramebufferTextureLayer( + GL_READ_FRAMEBUFFER, glAttachment, + texture->GetTextureHandle(), src.mipLevel, + dchecked_cast<uint32_t>(src.origin.z + z))); DAWN_GL_TRY(gl, ReadPixels(dchecked_cast<uint32_t>(src.origin.x), dchecked_cast<uint32_t>(src.origin.y), dchecked_cast<uint32_t>(copySize.width), @@ -1104,9 +1104,9 @@ } else { DAWN_TRY(dstTexture->EnsureSubresourceContentInitialized(gl, dstRange)); } - DAWN_TRY(CopyImageSubData(gl, src.aspect, srcTexture->GetHandle(), + DAWN_TRY(CopyImageSubData(gl, src.aspect, srcTexture->GetTextureHandle(), srcTexture->GetGLTarget(), src.mipLevel, - src.origin.ToOrigin3D(), dstTexture->GetHandle(), + src.origin.ToOrigin3D(), dstTexture->GetTextureHandle(), dstTexture->GetGLTarget(), dst.mipLevel, dst.origin.ToOrigin3D(), copySize.ToExtent3D())); break; @@ -1728,7 +1728,7 @@ GLenum target = texture->GetGLTarget(); data = DAWN_UNSAFE_TODO(static_cast<const uint8_t*>(data) + dataLayout.offset); DAWN_GL_TRY(gl, ActiveTexture(GL_TEXTURE0)); - DAWN_GL_TRY(gl, BindTexture(target, texture->GetHandle())); + DAWN_GL_TRY(gl, BindTexture(target, texture->GetTextureHandle())); const TypedTexelBlockInfo& blockInfo = GetBlockInfo(destination); const BlockExtent3D blockCopySize = blockInfo.ToBlock(copySize); const uint64_t bytesPerImage = dataLayout.rowsPerImage * dataLayout.bytesPerRow;
diff --git a/src/dawn/native/opengl/PhysicalDeviceGL.cpp b/src/dawn/native/opengl/PhysicalDeviceGL.cpp index 426e011..1f66c9f 100644 --- a/src/dawn/native/opengl/PhysicalDeviceGL.cpp +++ b/src/dawn/native/opengl/PhysicalDeviceGL.cpp
@@ -310,6 +310,8 @@ if (SupportTextureComponentSwizzle()) { EnableFeature(Feature::TextureComponentSwizzle); } + + EnableFeature(Feature::TransientAttachments); } namespace {
diff --git a/src/dawn/native/opengl/TextureGL.cpp b/src/dawn/native/opengl/TextureGL.cpp index f97f4f1..cbb43ba 100644 --- a/src/dawn/native/opengl/TextureGL.cpp +++ b/src/dawn/native/opengl/TextureGL.cpp
@@ -262,28 +262,49 @@ // Wrap the handle in a Texture class early so that it is deleted if initialization fails Ref<Texture> texture = AcquireRef(new Texture(device, descriptor, 0, OwnsHandle::Yes)); - bool clear = device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting); - DAWN_TRY(device->EnqueueGL([texture, clear](const OpenGLFunctions& gl) -> MaybeError { - DAWN_GL_TRY(gl, GenTextures(1, &texture->mHandle)); + if (texture->IsRenderbuffer()) { + DAWN_TRY(device->EnqueueGL([texture](const OpenGLFunctions& gl) -> MaybeError { + DAWN_GL_TRY(gl, GenRenderbuffers(1, &texture->mRenderbufferHandle)); - GLenum target = texture->GetGLTarget(); - uint32_t levels = texture->GetNumMipLevels(); - const GLFormat& glFormat = texture->GetGLFormat(); + const GLFormat& glFormat = texture->GetGLFormat(); - DAWN_GL_TRY(gl, BindTexture(target, texture->mHandle)); - DAWN_TRY(AllocateTexture(gl, target, texture->GetSampleCount(), levels, glFormat, - texture->GetBaseSize())); + DAWN_GL_TRY(gl, BindRenderbuffer(GL_RENDERBUFFER, texture->mRenderbufferHandle)); + auto width = texture->GetBaseSize().width; + auto height = texture->GetBaseSize().height; + if (texture->GetSampleCount() > 1) { + DAWN_GL_TRY( + gl, RenderbufferStorageMultisample(GL_RENDERBUFFER, texture->GetSampleCount(), + glFormat.internalFormat, width, height)); + } else { + DAWN_GL_TRY(gl, RenderbufferStorage(GL_RENDERBUFFER, glFormat.internalFormat, width, + height)); + } + return {}; + })); + } else { + bool clear = device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting); + DAWN_TRY(device->EnqueueGL([texture, clear](const OpenGLFunctions& gl) -> MaybeError { + DAWN_GL_TRY(gl, GenTextures(1, &texture->mTextureHandle)); - // The texture is not complete if it uses mipmapping and not all levels up to - // MAX_LEVEL have been defined. - DAWN_GL_TRY(gl, TexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels - 1)); + GLenum target = texture->GetGLTarget(); + uint32_t levels = texture->GetNumMipLevels(); + const GLFormat& glFormat = texture->GetGLFormat(); - if (clear) { - DAWN_TRY(texture->ClearTexture(gl, texture->GetAllSubresources(), - TextureBase::ClearValue::NonZero)); - } - return {}; - })); + DAWN_GL_TRY(gl, BindTexture(target, texture->mTextureHandle)); + DAWN_TRY(AllocateTexture(gl, target, texture->GetSampleCount(), levels, glFormat, + texture->GetBaseSize())); + + // The texture is not complete if it uses mipmapping and not all levels up to + // MAX_LEVEL have been defined. + DAWN_GL_TRY(gl, TexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels - 1)); + + if (clear) { + DAWN_TRY(texture->ClearTexture(gl, texture->GetAllSubresources(), + TextureBase::ClearValue::NonZero)); + } + return {}; + })); + } return std::move(texture); } @@ -296,7 +317,7 @@ Ref<Texture> texture = AcquireRef(new Texture(device, descriptor, 0, OwnsHandle::Yes)); DAWN_TRY(device->EnqueueGL([texture, memory = Ref<SharedTextureMemory>(memory)]( const OpenGLFunctions& gl) -> MaybeError { - DAWN_TRY_ASSIGN(texture->mHandle, memory->GenerateGLTexture(gl)); + DAWN_TRY_ASSIGN(texture->mTextureHandle, memory->GenerateGLTexture(gl)); return {}; })); @@ -306,30 +327,54 @@ Texture::Texture(Device* device, const UnpackedPtr<TextureDescriptor>& descriptor, - GLuint handle, + GLuint textureHandle, OwnsHandle ownsHandle) - : TextureBase(device, descriptor), mHandle(handle), mOwnsHandle(ownsHandle) { + : TextureBase(device, descriptor), + mTextureHandle(textureHandle), + mRenderbufferHandle(0), + mOwnsHandle(ownsHandle) { mTarget = TargetForTextureViewDimension(GetCompatibilityTextureBindingViewDimension(), descriptor->sampleCount); } Texture::~Texture() {} +bool Texture::IsRenderbuffer() const { + return ((GetUsage() & wgpu::TextureUsage::TransientAttachment) != 0) && + GetFormat().HasDepthOrStencil(); +} + void Texture::DestroyImpl(DestroyReason reason) { TextureBase::DestroyImpl(reason); if (mOwnsHandle == OwnsHandle::Yes) { - IgnoreErrors( - ToBackend(GetDevice()) - ->EnqueueDestroyGL(this, &Texture::GetHandle, reason, - [](const OpenGLFunctions& gl, GLuint handle) -> MaybeError { - DAWN_GL_TRY_IGNORE_ERRORS(gl, DeleteTextures(1, &handle)); - return {}; - })); + if (IsRenderbuffer()) { + IgnoreErrors(ToBackend(GetDevice()) + ->EnqueueDestroyGL( + this, &Texture::GetRenderbufferHandle, reason, + [](const OpenGLFunctions& gl, GLuint handle) -> MaybeError { + DAWN_GL_TRY_IGNORE_ERRORS(gl, DeleteRenderbuffers(1, &handle)); + return {}; + })); + } else { + IgnoreErrors(ToBackend(GetDevice()) + ->EnqueueDestroyGL( + this, &Texture::GetTextureHandle, reason, + [](const OpenGLFunctions& gl, GLuint handle) -> MaybeError { + DAWN_GL_TRY_IGNORE_ERRORS(gl, DeleteTextures(1, &handle)); + return {}; + })); + } } } -GLuint Texture::GetHandle() const { - return mHandle; +GLuint Texture::GetTextureHandle() const { + DAWN_ASSERT(mTextureHandle != 0 && !IsRenderbuffer()); + return mTextureHandle; +} + +GLuint Texture::GetRenderbufferHandle() const { + DAWN_ASSERT(mRenderbufferHandle != 0 && IsRenderbuffer()); + return mRenderbufferHandle; } GLenum Texture::GetGLTarget() const { @@ -343,6 +388,7 @@ MaybeError Texture::ClearTexture(const OpenGLFunctions& gl, const SubresourceRange& range, TextureBase::ClearValue clearValue) { + DAWN_ASSERT(!IsRenderbuffer()); Device* device = ToBackend(GetDevice()); uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1; @@ -407,7 +453,7 @@ continue; } DAWN_TRY(FramebufferTextureHelper(gl, mTarget, GL_DRAW_FRAMEBUFFER, attachment, - GetHandle(), level, layer)); + GetTextureHandle(), level, layer)); DAWN_TRY(DoClear(aspectsToClear)); } } @@ -455,13 +501,14 @@ continue; } if (gl.IsAtLeastGL(4, 4)) { - DAWN_GL_TRY(gl, ClearTexSubImage(mHandle, static_cast<GLint>(level), 0, 0, - static_cast<GLint>(layer), mipSize.width, - mipSize.height, mipSize.depthOrArrayLayers, - glFormat.format, glFormat.type, - clearValue == TextureBase::ClearValue::Zero - ? kClearColorDataBytes0.data() - : kClearColorDataBytes255.data())); + DAWN_GL_TRY( + gl, ClearTexSubImage(mTextureHandle, static_cast<GLint>(level), 0, 0, + static_cast<GLint>(layer), mipSize.width, + mipSize.height, mipSize.depthOrArrayLayers, + glFormat.format, glFormat.type, + clearValue == TextureBase::ClearValue::Zero + ? kClearColorDataBytes0.data() + : kClearColorDataBytes255.data())); continue; } @@ -512,12 +559,13 @@ .depthOrArrayLayers; for (GLint z = 0; z < static_cast<GLint>(depth); ++z) { DAWN_GL_TRY(gl, FramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attachment, - GetHandle(), level, z)); + GetTextureHandle(), level, z)); DAWN_TRY(DoClear()); } } else { DAWN_TRY(FramebufferTextureHelper(gl, mTarget, GL_DRAW_FRAMEBUFFER, - attachment, GetHandle(), level, layer)); + attachment, GetTextureHandle(), level, + layer)); DAWN_TRY(DoClear()); } @@ -646,19 +694,24 @@ ->EnqueueGL([view, texture = Ref<Texture>(ToBackend(texture))]( const OpenGLFunctions& gl) -> MaybeError { if (texture->IsDestroyed()) { - view->mHandle = 0; + view->mTextureHandle = 0; + view->mRenderbufferHandle = 0; } else if (view->mOwnsHandle == OwnsHandle::Yes) { GLuint handle = 0; DAWN_ASSERT(gl.IsAtLeastGL(4, 3)); DAWN_GL_TRY(gl, GenTextures(1, &handle)); - DAWN_GL_TRY(gl, - TextureView(handle, view->GetGLTarget(), texture->GetHandle(), - view->GetInternalFormat(), view->GetBaseMipLevel(), - view->GetLevelCount(), view->GetBaseArrayLayer(), - view->GetLayerCount())); - view->mHandle = handle; + DAWN_GL_TRY( + gl, TextureView(handle, view->GetGLTarget(), + texture->GetTextureHandle(), view->GetInternalFormat(), + view->GetBaseMipLevel(), view->GetLevelCount(), + view->GetBaseArrayLayer(), view->GetLayerCount())); + view->mTextureHandle = handle; } else { - view->mHandle = texture->GetHandle(); + if (texture->IsRenderbuffer()) { + view->mRenderbufferHandle = texture->GetRenderbufferHandle(); + } else { + view->mTextureHandle = texture->GetTextureHandle(); + } } return {}; })); @@ -677,7 +730,7 @@ if (mOwnsHandle == OwnsHandle::Yes) { IgnoreErrors( ToBackend(GetDevice()) - ->EnqueueDestroyGL(this, &TextureView::GetHandle, reason, + ->EnqueueDestroyGL(this, &TextureView::GetTextureHandle, reason, [](const OpenGLFunctions& gl, GLuint handle) -> MaybeError { DAWN_GL_TRY_IGNORE_ERRORS(gl, DeleteTextures(1, &handle)); return {}; @@ -685,9 +738,14 @@ } } -GLuint TextureView::GetHandle() const { - DAWN_ASSERT(mHandle != 0); - return mHandle; +GLuint TextureView::GetTextureHandle() const { + DAWN_ASSERT(mTextureHandle != 0); + return mTextureHandle; +} + +GLuint TextureView::GetRenderbufferHandle() const { + DAWN_ASSERT(mRenderbufferHandle != 0); + return mRenderbufferHandle; } GLenum TextureView::GetGLTarget() const { @@ -701,6 +759,16 @@ DAWN_ASSERT(depthSlice < static_cast<GLuint>(GetSingleSubresourceVirtualSize().depthOrArrayLayers)); + if (ToBackend(GetTexture())->IsRenderbuffer()) { + DAWN_ASSERT(GetDimension() == wgpu::TextureViewDimension::e2D); + DAWN_ASSERT(GetBaseMipLevel() == 0 && GetLevelCount() == 1); + DAWN_ASSERT(GetBaseArrayLayer() == 0 && GetLayerCount() == 1); + + GLuint handle = ToBackend(GetTexture())->GetRenderbufferHandle(); + DAWN_GL_TRY(gl, FramebufferRenderbuffer(target, attachment, GL_RENDERBUFFER, handle)); + return {}; + } + // Use the base texture where possible to minimize the amount of copying required on GLES. bool useOwnView = GetFormat().format != GetTexture()->GetFormat().format && !GetTexture()->GetFormat().HasDepthOrStencil(); @@ -710,14 +778,14 @@ if (useOwnView) { // Use our own texture handle and target which points to a subset of the texture's // subresources. - textureHandle = GetHandle(); + textureHandle = GetTextureHandle(); textarget = GetGLTarget(); mipLevel = 0; arrayLayer = 0; } else { // Use the texture's handle and target, with the view's base mip level and base array - textureHandle = ToBackend(GetTexture())->GetHandle(); + textureHandle = ToBackend(GetTexture())->GetTextureHandle(); textarget = ToBackend(GetTexture())->GetGLTarget(); mipLevel = GetBaseMipLevel(); // We have validated that the depthSlice in render pass's colorAttachments must be undefined
diff --git a/src/dawn/native/opengl/TextureGL.h b/src/dawn/native/opengl/TextureGL.h index 3fd3604..04cefe0 100644 --- a/src/dawn/native/opengl/TextureGL.h +++ b/src/dawn/native/opengl/TextureGL.h
@@ -56,7 +56,8 @@ GLuint handle, OwnsHandle ownsHandle); - GLuint GetHandle() const; + GLuint GetTextureHandle() const; + GLuint GetRenderbufferHandle() const; GLenum GetGLTarget() const; const GLFormat& GetGLFormat() const; @@ -64,6 +65,7 @@ const SubresourceRange& range); MaybeError SynchronizeTextureBeforeUse(); + bool IsRenderbuffer() const; private: ~Texture() override; @@ -73,7 +75,8 @@ const SubresourceRange& range, TextureBase::ClearValue clearValue); - GLuint mHandle; + GLuint mTextureHandle; + GLuint mRenderbufferHandle; OwnsHandle mOwnsHandle = OwnsHandle::No; GLenum mTarget; }; @@ -84,7 +87,8 @@ TextureBase* texture, const UnpackedPtr<TextureViewDescriptor>& descriptor); - GLuint GetHandle() const; + GLuint GetTextureHandle() const; + GLuint GetRenderbufferHandle() const; GLenum GetGLTarget() const; MaybeError BindToFramebuffer(const OpenGLFunctions& gl, GLenum target, @@ -98,8 +102,8 @@ void DestroyImpl(DestroyReason reason) override; GLenum GetInternalFormat() const; - // TODO(crbug.com/dawn/1355): Delete this handle on texture destroy. - GLuint mHandle; + GLuint mTextureHandle; + GLuint mRenderbufferHandle; GLenum mTarget; OwnsHandle mOwnsHandle = OwnsHandle::No; };
diff --git a/src/dawn/tests/end2end/ArchTierLimitsExhaustive.cpp b/src/dawn/tests/end2end/ArchTierLimitsExhaustive.cpp index 8bb5c63..4932041 100644 --- a/src/dawn/tests/end2end/ArchTierLimitsExhaustive.cpp +++ b/src/dawn/tests/end2end/ArchTierLimitsExhaustive.cpp
@@ -514,8 +514,8 @@ AddDevice({1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1}, "Vulkan_Qualcomm_R__Adreno_TM__X1_85_GPU"); // SwiftShader -AddDevice({0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "OpenGLES_ANGLE__Google__Vulkan_1_3_0__SwiftShader_Device__Subzero___0x0000C0DE____SwiftShader_driver_5_0_0__compat"); -AddDevice({0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "OpenGLES_ANGLE__Google__Vulkan_1_3_0__SwiftShader_Device__Subzero___0x0000C0DE____SwiftShader_driver_5_0_0__compat"); +AddDevice({0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "OpenGLES_ANGLE__Google__Vulkan_1_3_0__SwiftShader_Device__Subzero___0x0000C0DE____SwiftShader_driver_5_0_0__compat"); +AddDevice({0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "OpenGLES_ANGLE__Google__Vulkan_1_3_0__SwiftShader_Device__Subzero___0x0000C0DE____SwiftShader_driver_5_0_0__compat"); } // clang-format on