Support 2D array texture copying on D3D12, Metal and OpenGL
This patch implements the creation of 2D array textures and copying
between a buffer and a layer of a 2D array texture on D3D12, Metal
and OpenGL back-ends.
TEST=dawn_end2end_tests
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
index e4d1975..2dab383 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
@@ -333,7 +333,9 @@
D3D12_TEXTURE_COPY_LOCATION textureLocation;
textureLocation.pResource = texture->GetD3D12Resource();
textureLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
- textureLocation.SubresourceIndex = copy->destination.level;
+ textureLocation.SubresourceIndex =
+ texture->GetNumMipLevels() * copy->destination.slice +
+ copy->destination.level;
for (uint32_t i = 0; i < copySplit.count; ++i) {
auto& info = copySplit.copies[i];
@@ -379,7 +381,8 @@
D3D12_TEXTURE_COPY_LOCATION textureLocation;
textureLocation.pResource = texture->GetD3D12Resource();
textureLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
- textureLocation.SubresourceIndex = copy->source.level;
+ textureLocation.SubresourceIndex =
+ texture->GetNumMipLevels() * copy->source.slice + copy->source.level;
for (uint32_t i = 0; i < copySplit.count; ++i) {
auto& info = copySplit.copies[i];
diff --git a/src/dawn_native/d3d12/TextureCopySplitter.cpp b/src/dawn_native/d3d12/TextureCopySplitter.cpp
index 7b35597..32f5309 100644
--- a/src/dawn_native/d3d12/TextureCopySplitter.cpp
+++ b/src/dawn_native/d3d12/TextureCopySplitter.cpp
@@ -50,7 +50,7 @@
TextureCopySplit copy;
if (z != 0 || depth > 1) {
- // TODO(enga@google.com): Handle 3D / 2D arrays
+ // TODO(enga@google.com): Handle 3D
ASSERT(false);
return copy;
}
diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp
index 81e04ae..4c16c20 100644
--- a/src/dawn_native/d3d12/TextureD3D12.cpp
+++ b/src/dawn_native/d3d12/TextureD3D12.cpp
@@ -114,7 +114,7 @@
resourceDescriptor.Alignment = 0;
resourceDescriptor.Width = GetWidth();
resourceDescriptor.Height = GetHeight();
- resourceDescriptor.DepthOrArraySize = static_cast<UINT16>(GetDepth());
+ resourceDescriptor.DepthOrArraySize = GetDepthOrArraySize();
resourceDescriptor.MipLevels = static_cast<UINT16>(GetNumMipLevels());
resourceDescriptor.Format = D3D12TextureFormat(GetFormat());
resourceDescriptor.SampleDesc.Count = 1;
@@ -151,6 +151,15 @@
return mResourcePtr;
}
+ UINT16 Texture::GetDepthOrArraySize() {
+ switch (GetDimension()) {
+ case dawn::TextureDimension::e2D:
+ return static_cast<UINT16>(GetArrayLayers());
+ default:
+ UNREACHABLE();
+ }
+ }
+
void Texture::TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList,
dawn::TextureUsageBit usage) {
// Avoid transitioning the texture when it isn't needed.
diff --git a/src/dawn_native/d3d12/TextureD3D12.h b/src/dawn_native/d3d12/TextureD3D12.h
index 571681b..8492ba6 100644
--- a/src/dawn_native/d3d12/TextureD3D12.h
+++ b/src/dawn_native/d3d12/TextureD3D12.h
@@ -38,6 +38,8 @@
dawn::TextureUsageBit usage);
private:
+ UINT16 GetDepthOrArraySize();
+
ComPtr<ID3D12Resource> mResource = {};
ID3D12Resource* mResourcePtr = nullptr;
dawn::TextureUsageBit mLastUsage = dawn::TextureUsageBit::None;
diff --git a/src/dawn_native/metal/CommandBufferMTL.mm b/src/dawn_native/metal/CommandBufferMTL.mm
index 4fd66a3..746f298 100644
--- a/src/dawn_native/metal/CommandBufferMTL.mm
+++ b/src/dawn_native/metal/CommandBufferMTL.mm
@@ -270,7 +270,7 @@
sourceBytesPerImage:(copy->rowPitch * dst.height)
sourceSize:size
toTexture:texture->GetMTLTexture()
- destinationSlice:0
+ destinationSlice:dst.slice
destinationLevel:dst.level
destinationOrigin:origin];
} break;
@@ -294,7 +294,7 @@
encoders.EnsureBlit(commandBuffer);
[encoders.blit copyFromTexture:texture->GetMTLTexture()
- sourceSlice:0
+ sourceSlice:src.slice
sourceLevel:src.level
sourceOrigin:origin
sourceSize:size
diff --git a/src/dawn_native/metal/TextureMTL.mm b/src/dawn_native/metal/TextureMTL.mm
index 3c3a1c2..40f9c0f 100644
--- a/src/dawn_native/metal/TextureMTL.mm
+++ b/src/dawn_native/metal/TextureMTL.mm
@@ -58,10 +58,11 @@
return result;
}
- MTLTextureType MetalTextureType(dawn::TextureDimension dimension) {
+ MTLTextureType MetalTextureType(dawn::TextureDimension dimension,
+ unsigned int arrayLayers) {
switch (dimension) {
case dawn::TextureDimension::e2D:
- return MTLTextureType2D;
+ return (arrayLayers > 1) ? MTLTextureType2DArray : MTLTextureType2D;
}
}
}
@@ -70,14 +71,14 @@
: TextureBase(device, descriptor) {
auto desc = [MTLTextureDescriptor new];
[desc autorelease];
- desc.textureType = MetalTextureType(GetDimension());
+ desc.textureType = MetalTextureType(GetDimension(), GetArrayLayers());
desc.usage = MetalTextureUsage(GetUsage());
desc.pixelFormat = MetalPixelFormat(GetFormat());
desc.width = GetWidth();
desc.height = GetHeight();
desc.depth = GetDepth();
desc.mipmapLevelCount = GetNumMipLevels();
- desc.arrayLength = 1;
+ desc.arrayLength = GetArrayLayers();
desc.storageMode = MTLStorageModePrivate;
auto mtlDevice = device->GetMTLDevice();
diff --git a/src/dawn_native/opengl/CommandBufferGL.cpp b/src/dawn_native/opengl/CommandBufferGL.cpp
index 054a899..2f3737b 100644
--- a/src/dawn_native/opengl/CommandBufferGL.cpp
+++ b/src/dawn_native/opengl/CommandBufferGL.cpp
@@ -331,13 +331,28 @@
glActiveTexture(GL_TEXTURE0);
glBindTexture(target, texture->GetHandle());
- ASSERT(texture->GetDimension() == dawn::TextureDimension::e2D);
glPixelStorei(GL_UNPACK_ROW_LENGTH,
copy->rowPitch / TextureFormatPixelSize(texture->GetFormat()));
- glTexSubImage2D(target, dst.level, dst.x, dst.y, dst.width, dst.height,
+ switch (texture->GetDimension()) {
+ case dawn::TextureDimension::e2D:
+ if (texture->GetArrayLayers() > 1) {
+ glTexSubImage3D(
+ target, dst.level, dst.x, dst.y, dst.slice, dst.width,
+ dst.height, 1, format.format, format.type,
+ reinterpret_cast<void*>(static_cast<uintptr_t>(src.offset)));
+ } else {
+ glTexSubImage2D(
+ target, dst.level, dst.x, dst.y, dst.width, dst.height,
format.format, format.type,
reinterpret_cast<void*>(static_cast<uintptr_t>(src.offset)));
+ }
+ break;
+
+ default:
+ UNREACHABLE();
+ }
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
} break;
@@ -348,18 +363,31 @@
Texture* texture = ToBackend(src.texture.Get());
Buffer* buffer = ToBackend(dst.buffer.Get());
auto format = texture->GetGLFormat();
+ GLenum target = texture->GetGLTarget();
// 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.
- ASSERT(texture->GetDimension() == dawn::TextureDimension::e2D);
- glBindTexture(GL_TEXTURE_2D, texture->GetHandle());
+ glBindTexture(target, texture->GetHandle());
GLuint readFBO = 0;
glGenFramebuffers(1, &readFBO);
glBindFramebuffer(GL_READ_FRAMEBUFFER, readFBO);
+ switch (texture->GetDimension()) {
+ case dawn::TextureDimension::e2D:
+ if (texture->GetArrayLayers() > 1) {
+ glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ texture->GetHandle(), src.level,
+ src.slice);
+ } else {
+ glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, texture->GetHandle(),
+ src.level);
+ }
+ break;
- glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- texture->GetHandle(), src.level);
+ default:
+ UNREACHABLE();
+ }
glBindBuffer(GL_PIXEL_PACK_BUFFER, buffer->GetHandle());
glPixelStorei(GL_PACK_ROW_LENGTH,
diff --git a/src/dawn_native/opengl/TextureGL.cpp b/src/dawn_native/opengl/TextureGL.cpp
index 12cadc8..9033f6d 100644
--- a/src/dawn_native/opengl/TextureGL.cpp
+++ b/src/dawn_native/opengl/TextureGL.cpp
@@ -24,10 +24,11 @@
namespace {
- GLenum TargetForDimension(dawn::TextureDimension dimension) {
+ GLenum TargetForDimensionAndArrayLayers(dawn::TextureDimension dimension,
+ uint32_t arrayLayer) {
switch (dimension) {
case dawn::TextureDimension::e2D:
- return GL_TEXTURE_2D;
+ return (arrayLayer > 1) ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
default:
UNREACHABLE();
}
@@ -74,19 +75,32 @@
Texture::Texture(Device* device, const TextureDescriptor* descriptor, GLuint handle)
: TextureBase(device, descriptor), mHandle(handle) {
- mTarget = TargetForDimension(GetDimension());
+ mTarget = TargetForDimensionAndArrayLayers(GetDimension(), GetArrayLayers());
uint32_t width = GetWidth();
uint32_t height = GetHeight();
uint32_t levels = GetNumMipLevels();
+ uint32_t arrayLayers = GetArrayLayers();
auto formatInfo = GetGLFormatInfo(GetFormat());
glBindTexture(mTarget, handle);
for (uint32_t i = 0; i < levels; ++i) {
- glTexImage2D(mTarget, i, formatInfo.internalFormat, width, height, 0, formatInfo.format,
- formatInfo.type, nullptr);
+ switch (GetDimension()) {
+ case dawn::TextureDimension::e2D:
+ if (arrayLayers > 1) {
+ glTexImage3D(mTarget, i, formatInfo.internalFormat, width, height,
+ arrayLayers, 0, formatInfo.format, formatInfo.type, nullptr);
+ } else {
+ glTexImage2D(mTarget, i, formatInfo.internalFormat, width, height, 0,
+ formatInfo.format, formatInfo.type, nullptr);
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+
width = std::max(uint32_t(1), width / 2);
height = std::max(uint32_t(1), height / 2);
}
diff --git a/src/tests/end2end/CopyTests.cpp b/src/tests/end2end/CopyTests.cpp
index 912d8b6..c4780fe 100644
--- a/src/tests/end2end/CopyTests.cpp
+++ b/src/tests/end2end/CopyTests.cpp
@@ -365,11 +365,7 @@
}
// Test that copying regions of each texture 2D array layer works
-TEST_P(CopyTests_T2B, Texture2DArrayRegion)
-{
- // TODO(jiawei.shao@intel.com): support 2D array texture on OpenGL, D3D12 and Metal.
- DAWN_SKIP_TEST_IF(IsOpenGL() || IsD3D12() || IsMetal());
-
+TEST_P(CopyTests_T2B, Texture2DArrayRegion) {
constexpr uint32_t kWidth = 256;
constexpr uint32_t kHeight = 128;
constexpr uint32_t kLayers = 6u;
@@ -378,9 +374,6 @@
// Test that copying texture 2D array mips with 256-byte aligned sizes works
TEST_P(CopyTests_T2B, Texture2DArrayMip) {
- // TODO(jiawei.shao@intel.com): support 2D array texture on OpenGL, D3D12 and Metal.
- DAWN_SKIP_TEST_IF(IsOpenGL() || IsD3D12() || IsMetal());
-
constexpr uint32_t kWidth = 256;
constexpr uint32_t kHeight = 128;
constexpr uint32_t kLayers = 6u;