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
This commit is contained in:
Jiawei Shao 2018-08-30 17:32:35 -07:00 committed by Corentin Wallez
parent d8597b2e1f
commit 4ccf4e3fdd
9 changed files with 79 additions and 29 deletions

View File

@ -333,7 +333,9 @@ namespace dawn_native { namespace d3d12 {
D3D12_TEXTURE_COPY_LOCATION textureLocation; D3D12_TEXTURE_COPY_LOCATION textureLocation;
textureLocation.pResource = texture->GetD3D12Resource(); textureLocation.pResource = texture->GetD3D12Resource();
textureLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; 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) { for (uint32_t i = 0; i < copySplit.count; ++i) {
auto& info = copySplit.copies[i]; auto& info = copySplit.copies[i];
@ -379,7 +381,8 @@ namespace dawn_native { namespace d3d12 {
D3D12_TEXTURE_COPY_LOCATION textureLocation; D3D12_TEXTURE_COPY_LOCATION textureLocation;
textureLocation.pResource = texture->GetD3D12Resource(); textureLocation.pResource = texture->GetD3D12Resource();
textureLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; 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) { for (uint32_t i = 0; i < copySplit.count; ++i) {
auto& info = copySplit.copies[i]; auto& info = copySplit.copies[i];

View File

@ -50,7 +50,7 @@ namespace dawn_native { namespace d3d12 {
TextureCopySplit copy; TextureCopySplit copy;
if (z != 0 || depth > 1) { if (z != 0 || depth > 1) {
// TODO(enga@google.com): Handle 3D / 2D arrays // TODO(enga@google.com): Handle 3D
ASSERT(false); ASSERT(false);
return copy; return copy;
} }

View File

@ -114,7 +114,7 @@ namespace dawn_native { namespace d3d12 {
resourceDescriptor.Alignment = 0; resourceDescriptor.Alignment = 0;
resourceDescriptor.Width = GetWidth(); resourceDescriptor.Width = GetWidth();
resourceDescriptor.Height = GetHeight(); resourceDescriptor.Height = GetHeight();
resourceDescriptor.DepthOrArraySize = static_cast<UINT16>(GetDepth()); resourceDescriptor.DepthOrArraySize = GetDepthOrArraySize();
resourceDescriptor.MipLevels = static_cast<UINT16>(GetNumMipLevels()); resourceDescriptor.MipLevels = static_cast<UINT16>(GetNumMipLevels());
resourceDescriptor.Format = D3D12TextureFormat(GetFormat()); resourceDescriptor.Format = D3D12TextureFormat(GetFormat());
resourceDescriptor.SampleDesc.Count = 1; resourceDescriptor.SampleDesc.Count = 1;
@ -151,6 +151,15 @@ namespace dawn_native { namespace d3d12 {
return mResourcePtr; return mResourcePtr;
} }
UINT16 Texture::GetDepthOrArraySize() {
switch (GetDimension()) {
case dawn::TextureDimension::e2D:
return static_cast<UINT16>(GetArrayLayers());
default:
UNREACHABLE();
}
}
void Texture::TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList, void Texture::TransitionUsageNow(ComPtr<ID3D12GraphicsCommandList> commandList,
dawn::TextureUsageBit usage) { dawn::TextureUsageBit usage) {
// Avoid transitioning the texture when it isn't needed. // Avoid transitioning the texture when it isn't needed.

View File

@ -38,6 +38,8 @@ namespace dawn_native { namespace d3d12 {
dawn::TextureUsageBit usage); dawn::TextureUsageBit usage);
private: private:
UINT16 GetDepthOrArraySize();
ComPtr<ID3D12Resource> mResource = {}; ComPtr<ID3D12Resource> mResource = {};
ID3D12Resource* mResourcePtr = nullptr; ID3D12Resource* mResourcePtr = nullptr;
dawn::TextureUsageBit mLastUsage = dawn::TextureUsageBit::None; dawn::TextureUsageBit mLastUsage = dawn::TextureUsageBit::None;

View File

@ -270,7 +270,7 @@ namespace dawn_native { namespace metal {
sourceBytesPerImage:(copy->rowPitch * dst.height) sourceBytesPerImage:(copy->rowPitch * dst.height)
sourceSize:size sourceSize:size
toTexture:texture->GetMTLTexture() toTexture:texture->GetMTLTexture()
destinationSlice:0 destinationSlice:dst.slice
destinationLevel:dst.level destinationLevel:dst.level
destinationOrigin:origin]; destinationOrigin:origin];
} break; } break;
@ -294,7 +294,7 @@ namespace dawn_native { namespace metal {
encoders.EnsureBlit(commandBuffer); encoders.EnsureBlit(commandBuffer);
[encoders.blit copyFromTexture:texture->GetMTLTexture() [encoders.blit copyFromTexture:texture->GetMTLTexture()
sourceSlice:0 sourceSlice:src.slice
sourceLevel:src.level sourceLevel:src.level
sourceOrigin:origin sourceOrigin:origin
sourceSize:size sourceSize:size

View File

@ -58,10 +58,11 @@ namespace dawn_native { namespace metal {
return result; return result;
} }
MTLTextureType MetalTextureType(dawn::TextureDimension dimension) { MTLTextureType MetalTextureType(dawn::TextureDimension dimension,
unsigned int arrayLayers) {
switch (dimension) { switch (dimension) {
case dawn::TextureDimension::e2D: case dawn::TextureDimension::e2D:
return MTLTextureType2D; return (arrayLayers > 1) ? MTLTextureType2DArray : MTLTextureType2D;
} }
} }
} }
@ -70,14 +71,14 @@ namespace dawn_native { namespace metal {
: TextureBase(device, descriptor) { : TextureBase(device, descriptor) {
auto desc = [MTLTextureDescriptor new]; auto desc = [MTLTextureDescriptor new];
[desc autorelease]; [desc autorelease];
desc.textureType = MetalTextureType(GetDimension()); desc.textureType = MetalTextureType(GetDimension(), GetArrayLayers());
desc.usage = MetalTextureUsage(GetUsage()); desc.usage = MetalTextureUsage(GetUsage());
desc.pixelFormat = MetalPixelFormat(GetFormat()); desc.pixelFormat = MetalPixelFormat(GetFormat());
desc.width = GetWidth(); desc.width = GetWidth();
desc.height = GetHeight(); desc.height = GetHeight();
desc.depth = GetDepth(); desc.depth = GetDepth();
desc.mipmapLevelCount = GetNumMipLevels(); desc.mipmapLevelCount = GetNumMipLevels();
desc.arrayLength = 1; desc.arrayLength = GetArrayLayers();
desc.storageMode = MTLStorageModePrivate; desc.storageMode = MTLStorageModePrivate;
auto mtlDevice = device->GetMTLDevice(); auto mtlDevice = device->GetMTLDevice();

View File

@ -331,13 +331,28 @@ namespace dawn_native { namespace opengl {
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(target, texture->GetHandle()); glBindTexture(target, texture->GetHandle());
ASSERT(texture->GetDimension() == dawn::TextureDimension::e2D);
glPixelStorei(GL_UNPACK_ROW_LENGTH, glPixelStorei(GL_UNPACK_ROW_LENGTH,
copy->rowPitch / TextureFormatPixelSize(texture->GetFormat())); 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, format.format, format.type,
reinterpret_cast<void*>(static_cast<uintptr_t>(src.offset))); reinterpret_cast<void*>(static_cast<uintptr_t>(src.offset)));
}
break;
default:
UNREACHABLE();
}
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
} break; } break;
@ -348,18 +363,31 @@ namespace dawn_native { namespace opengl {
Texture* texture = ToBackend(src.texture.Get()); Texture* texture = ToBackend(src.texture.Get());
Buffer* buffer = ToBackend(dst.buffer.Get()); Buffer* buffer = ToBackend(dst.buffer.Get());
auto format = texture->GetGLFormat(); auto format = texture->GetGLFormat();
GLenum target = texture->GetGLTarget();
// The only way to move data from a texture to a buffer in GL is via // 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. // glReadPixels with a pack buffer. Create a temporary FBO for the copy.
ASSERT(texture->GetDimension() == dawn::TextureDimension::e2D); glBindTexture(target, texture->GetHandle());
glBindTexture(GL_TEXTURE_2D, texture->GetHandle());
GLuint readFBO = 0; GLuint readFBO = 0;
glGenFramebuffers(1, &readFBO); glGenFramebuffers(1, &readFBO);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 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, default:
texture->GetHandle(), src.level); UNREACHABLE();
}
glBindBuffer(GL_PIXEL_PACK_BUFFER, buffer->GetHandle()); glBindBuffer(GL_PIXEL_PACK_BUFFER, buffer->GetHandle());
glPixelStorei(GL_PACK_ROW_LENGTH, glPixelStorei(GL_PACK_ROW_LENGTH,

View File

@ -24,10 +24,11 @@ namespace dawn_native { namespace opengl {
namespace { namespace {
GLenum TargetForDimension(dawn::TextureDimension dimension) { GLenum TargetForDimensionAndArrayLayers(dawn::TextureDimension dimension,
uint32_t arrayLayer) {
switch (dimension) { switch (dimension) {
case dawn::TextureDimension::e2D: case dawn::TextureDimension::e2D:
return GL_TEXTURE_2D; return (arrayLayer > 1) ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
default: default:
UNREACHABLE(); UNREACHABLE();
} }
@ -74,19 +75,32 @@ namespace dawn_native { namespace opengl {
Texture::Texture(Device* device, const TextureDescriptor* descriptor, GLuint handle) Texture::Texture(Device* device, const TextureDescriptor* descriptor, GLuint handle)
: TextureBase(device, descriptor), mHandle(handle) { : TextureBase(device, descriptor), mHandle(handle) {
mTarget = TargetForDimension(GetDimension()); mTarget = TargetForDimensionAndArrayLayers(GetDimension(), GetArrayLayers());
uint32_t width = GetWidth(); uint32_t width = GetWidth();
uint32_t height = GetHeight(); uint32_t height = GetHeight();
uint32_t levels = GetNumMipLevels(); uint32_t levels = GetNumMipLevels();
uint32_t arrayLayers = GetArrayLayers();
auto formatInfo = GetGLFormatInfo(GetFormat()); auto formatInfo = GetGLFormatInfo(GetFormat());
glBindTexture(mTarget, handle); glBindTexture(mTarget, handle);
for (uint32_t i = 0; i < levels; ++i) { for (uint32_t i = 0; i < levels; ++i) {
glTexImage2D(mTarget, i, formatInfo.internalFormat, width, height, 0, formatInfo.format, switch (GetDimension()) {
formatInfo.type, nullptr); 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); width = std::max(uint32_t(1), width / 2);
height = std::max(uint32_t(1), height / 2); height = std::max(uint32_t(1), height / 2);
} }

View File

@ -365,11 +365,7 @@ TEST_P(CopyTests_T2B, RowPitchUnaligned) {
} }
// Test that copying regions of each texture 2D array layer works // Test that copying regions of each texture 2D array layer works
TEST_P(CopyTests_T2B, Texture2DArrayRegion) 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());
constexpr uint32_t kWidth = 256; constexpr uint32_t kWidth = 256;
constexpr uint32_t kHeight = 128; constexpr uint32_t kHeight = 128;
constexpr uint32_t kLayers = 6u; constexpr uint32_t kLayers = 6u;
@ -378,9 +374,6 @@ TEST_P(CopyTests_T2B, Texture2DArrayRegion)
// Test that copying texture 2D array mips with 256-byte aligned sizes works // Test that copying texture 2D array mips with 256-byte aligned sizes works
TEST_P(CopyTests_T2B, Texture2DArrayMip) { 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 kWidth = 256;
constexpr uint32_t kHeight = 128; constexpr uint32_t kHeight = 128;
constexpr uint32_t kLayers = 6u; constexpr uint32_t kLayers = 6u;