Move image type logic to GuestTexture, allowing 2D array views for 3D RTs

We can't render to a 3D texture through a 3D view, we instead have to create a 2D array view into it and render to that. The texture manager previously didn't support having a different view type/layer count between a guest texture view and the underlying storage texture that is required to support this so that was also implemented by reading the view layer count from the dimensions depth instead if the underlying texture is 3D (and the view type is 2D array). Additionally move away from our own view type enum to Vulkan, inline with other guest texture member types.
This commit is contained in:
Billy Laws 2022-05-31 22:09:53 +01:00
parent 22695c4feb
commit c745e0e02b
6 changed files with 77 additions and 61 deletions

View File

@ -85,16 +85,15 @@ namespace skyline::gpu::interconnect {
texture.aspect = texture.format->vkAspect; texture.aspect = texture.format->vkAspect;
texture.baseArrayLayer = 0; texture.baseArrayLayer = 0;
texture.layerCount = 1; texture.layerCount = 1;
texture.viewType = vk::ImageViewType::e2D;
if (surface.memoryLayout == fermi2d::MemoryLayout::Pitch) { if (surface.memoryLayout == fermi2d::MemoryLayout::Pitch) {
texture.type = gpu::texture::TextureType::e2D;
texture.dimensions = gpu::texture::Dimensions{surface.stride / texture.format->bpb, surface.height, 1}; texture.dimensions = gpu::texture::Dimensions{surface.stride / texture.format->bpb, surface.height, 1};
texture.tileConfig = gpu::texture::TileConfig{ texture.tileConfig = gpu::texture::TileConfig{
.mode = gpu::texture::TileMode::Pitch, .mode = gpu::texture::TileMode::Pitch,
.pitch = surface.stride .pitch = surface.stride
}; };
} else { } else {
texture.type = gpu::texture::TextureType::e2D;
texture.dimensions = gpu::texture::Dimensions{surface.width, surface.height, surface.depth}; texture.dimensions = gpu::texture::Dimensions{surface.width, surface.height, surface.depth};
texture.tileConfig = gpu::texture::TileConfig{ texture.tileConfig = gpu::texture::TileConfig{
.mode = gpu::texture::TileMode::Block, .mode = gpu::texture::TileMode::Block,

View File

@ -402,7 +402,15 @@ namespace skyline::gpu::interconnect {
renderTarget.guest.mappings.assign(mappings.begin(), mappings.end()); renderTarget.guest.mappings.assign(mappings.begin(), mappings.end());
} }
renderTarget.guest.type = static_cast<texture::TextureType>(renderTarget.guest.dimensions.GetType()); renderTarget.guest.viewType = [&]() {
if (renderTarget.is3d)
return vk::ImageViewType::e2DArray; // We can't render to 3D textures, so render to a 2D array view of a 3D texture (since layerCount is 1 and depth is >1 the texture manager will create the underlying texture as such)
if (renderTarget.guest.layerCount > 1)
return vk::ImageViewType::e2DArray;
return vk::ImageViewType::e2D;
}();
renderTarget.view = gpu.texture.FindOrCreate(renderTarget.guest); renderTarget.view = gpu.texture.FindOrCreate(renderTarget.guest);
return renderTarget.view.get(); return renderTarget.view.get();
@ -2270,14 +2278,13 @@ namespace skyline::gpu::interconnect {
guest.viewMipCount = textureControl.viewConfig.mipMaxLevel - textureControl.viewConfig.mipMinLevel + 1; guest.viewMipCount = textureControl.viewConfig.mipMaxLevel - textureControl.viewConfig.mipMinLevel + 1;
using TicType = TextureImageControl::TextureType; using TicType = TextureImageControl::TextureType;
using TexType = texture::TextureType;
switch (textureControl.textureType) { switch (textureControl.textureType) {
case TicType::e1D: case TicType::e1D:
guest.type = TexType::e1D; guest.viewType = vk::ImageViewType::e1D;
guest.layerCount = 1; guest.layerCount = 1;
break; break;
case TicType::e1DArray: case TicType::e1DArray:
guest.type = TexType::e1DArray; guest.viewType = vk::ImageViewType::e1DArray;
guest.layerCount = depth; guest.layerCount = depth;
break; break;
case TicType::e1DBuffer: case TicType::e1DBuffer:
@ -2288,26 +2295,26 @@ namespace skyline::gpu::interconnect {
guest.viewMipBase = 0; guest.viewMipBase = 0;
guest.viewMipCount = 1; guest.viewMipCount = 1;
case TicType::e2D: case TicType::e2D:
guest.type = TexType::e2D; guest.viewType = vk::ImageViewType::e2D;
guest.layerCount = 1; guest.layerCount = 1;
break; break;
case TicType::e2DArray: case TicType::e2DArray:
guest.type = TexType::e2DArray; guest.viewType = vk::ImageViewType::e2DArray;
guest.layerCount = depth; guest.layerCount = depth;
break; break;
case TicType::e3D: case TicType::e3D:
guest.type = TexType::e3D; guest.viewType = vk::ImageViewType::e3D;
guest.layerCount = 1; guest.layerCount = 1;
guest.dimensions.depth = depth; guest.dimensions.depth = depth;
break; break;
case TicType::eCube: case TicType::eCube:
guest.type = TexType::eCube; guest.viewType = vk::ImageViewType::eCube;
guest.layerCount = CubeFaceCount; guest.layerCount = CubeFaceCount;
break; break;
case TicType::eCubeArray: case TicType::eCubeArray:
guest.type = TexType::eCubeArray; guest.viewType = vk::ImageViewType::eCubeArray;
guest.layerCount = depth * CubeFaceCount; guest.layerCount = depth * CubeFaceCount;
break; break;
} }

View File

@ -28,6 +28,40 @@ namespace skyline::gpu {
} }
} }
vk::ImageType GuestTexture::GetImageType() const {
switch (viewType) {
case vk::ImageViewType::e1D:
case vk::ImageViewType::e1DArray:
return vk::ImageType::e1D;
case vk::ImageViewType::e2D:
case vk::ImageViewType::e2DArray:
// If depth is > 1 this is a 2D view into a 3D texture so the underlying image needs to be created as 3D yoo
if (dimensions.depth > 1)
return vk::ImageType::e3D;
else
return vk::ImageType::e2D;
case vk::ImageViewType::eCube:
case vk::ImageViewType::eCubeArray:
return vk::ImageType::e2D;
case vk::ImageViewType::e3D:
return vk::ImageType::e3D;
}
}
u32 GuestTexture::GetViewLayerCount() const {
if (GetImageType() == vk::ImageType::e3D && viewType != vk::ImageViewType::e3D)
return dimensions.depth;
else
return layerCount;
}
u32 GuestTexture::GetViewDepth() const {
if (GetImageType() == vk::ImageType::e3D && viewType != vk::ImageViewType::e3D)
return layerCount;
else
return dimensions.depth;
}
TextureView::TextureView(std::shared_ptr<Texture> texture, vk::ImageViewType type, vk::ImageSubresourceRange range, texture::Format format, vk::ComponentMapping mapping) : texture(std::move(texture)), type(type), format(format), mapping(mapping), range(range) {} TextureView::TextureView(std::shared_ptr<Texture> texture, vk::ImageViewType type, vk::ImageSubresourceRange range, texture::Format format, vk::ComponentMapping mapping) : texture(std::move(texture)), type(type), format(format), mapping(mapping), range(range) {}
Texture::TextureViewStorage::TextureViewStorage(vk::ImageViewType type, texture::Format format, vk::ComponentMapping mapping, vk::ImageSubresourceRange range, vk::raii::ImageView &&vkView) : type(type), format(format), mapping(mapping), range(range), vkView(std::move(vkView)) {} Texture::TextureViewStorage::TextureViewStorage(vk::ImageViewType type, texture::Format format, vk::ComponentMapping mapping, vk::ImageSubresourceRange range, vk::raii::ImageView &&vkView) : type(type), format(format), mapping(mapping), range(range), vkView(std::move(vkView)) {}
@ -519,21 +553,7 @@ namespace skyline::gpu {
if (format->vkAspect & (vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil)) if (format->vkAspect & (vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil))
usage |= vk::ImageUsageFlagBits::eDepthStencilAttachment; usage |= vk::ImageUsageFlagBits::eDepthStencilAttachment;
// First attempt to derive type from dimensions auto imageType{guest->GetImageType()};
auto imageType{dimensions.GetType()};
// Try to ensure that the image type is compatible with the given image view type since we can't create a 2D image view from a 1D image
if (imageType == vk::ImageType::e1D && guest->type != texture::TextureType::e1D && guest->type != texture::TextureType::e1DArray) {
switch (guest->type) {
case texture::TextureType::e3D:
imageType = vk::ImageType::e3D;
break;
default:
imageType = vk::ImageType::e2D;
break;
}
}
if (imageType == vk::ImageType::e2D && dimensions.width == dimensions.height && layerCount >= 6) if (imageType == vk::ImageType::e2D && dimensions.width == dimensions.height && layerCount >= 6)
flags |= vk::ImageCreateFlagBits::eCubeCompatible; flags |= vk::ImageCreateFlagBits::eCubeCompatible;
else if (imageType == vk::ImageType::e3D) else if (imageType == vk::ImageType::e3D)

View File

@ -27,15 +27,6 @@ namespace skyline::gpu {
auto operator<=>(const Dimensions &) const = default; auto operator<=>(const Dimensions &) const = default;
constexpr vk::ImageType GetType() const {
if (depth > 1)
return vk::ImageType::e3D;
else if (height > 1)
return vk::ImageType::e2D;
else
return vk::ImageType::e1D;
}
constexpr operator vk::Extent2D() const { constexpr operator vk::Extent2D() const {
return vk::Extent2D{ return vk::Extent2D{
.width = width, .width = width,
@ -208,21 +199,6 @@ namespace skyline::gpu {
} }
}; };
/**
* @brief The type of a texture to determine the access patterns for it
* @note This is effectively the Tegra X1 texture types with the 1DBuffer + 2DNoMipmap removed as those are handled elsewhere
* @note We explicitly utilize Vulkan types here as it provides the most efficient conversion while not exposing Vulkan to the outer API
*/
enum class TextureType {
e1D = VK_IMAGE_VIEW_TYPE_1D,
e2D = VK_IMAGE_VIEW_TYPE_2D,
e3D = VK_IMAGE_VIEW_TYPE_3D,
eCube = VK_IMAGE_VIEW_TYPE_CUBE,
e1DArray = VK_IMAGE_VIEW_TYPE_1D_ARRAY,
e2DArray = VK_IMAGE_VIEW_TYPE_2D_ARRAY,
eCubeArray = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY,
};
/** /**
* @brief A description of a single mipmapped level of a block-linear surface * @brief A description of a single mipmapped level of a block-linear surface
*/ */
@ -250,7 +226,7 @@ namespace skyline::gpu {
texture::Dimensions dimensions{}; texture::Dimensions dimensions{};
texture::Format format{}; texture::Format format{};
texture::TileConfig tileConfig{}; texture::TileConfig tileConfig{};
texture::TextureType type{}; vk::ImageViewType viewType{};
u32 baseArrayLayer{}; u32 baseArrayLayer{};
u32 layerCount{1}; u32 layerCount{1};
u32 layerStride{}; //!< An optional hint regarding the size of a single layer, it **should** be set to 0 when not available and should never be a non-0 value that doesn't reflect the correct layer stride u32 layerStride{}; //!< An optional hint regarding the size of a single layer, it **should** be set to 0 when not available and should never be a non-0 value that doesn't reflect the correct layer stride
@ -262,12 +238,12 @@ namespace skyline::gpu {
GuestTexture() {} GuestTexture() {}
GuestTexture(Mappings mappings, texture::Dimensions dimensions, texture::Format format, texture::TileConfig tileConfig, texture::TextureType type, u32 baseArrayLayer = 0, u32 layerCount = 1, u32 layerStride = 0, u32 mipLevelCount = 1, u32 viewMipBase = 0, u32 viewMipCount = 1) GuestTexture(Mappings mappings, texture::Dimensions dimensions, texture::Format format, texture::TileConfig tileConfig, vk::ImageViewType viewType, u32 baseArrayLayer = 0, u32 layerCount = 1, u32 layerStride = 0, u32 mipLevelCount = 1, u32 viewMipBase = 0, u32 viewMipCount = 1)
: mappings(mappings), : mappings(mappings),
dimensions(dimensions), dimensions(dimensions),
format(format), format(format),
tileConfig(tileConfig), tileConfig(tileConfig),
type(type), viewType(viewType),
baseArrayLayer(baseArrayLayer), baseArrayLayer(baseArrayLayer),
layerCount(layerCount), layerCount(layerCount),
layerStride(layerStride), layerStride(layerStride),
@ -276,12 +252,12 @@ namespace skyline::gpu {
viewMipCount(viewMipCount), viewMipCount(viewMipCount),
aspect(format->vkAspect) {} aspect(format->vkAspect) {}
GuestTexture(span<u8> mapping, texture::Dimensions dimensions, texture::Format format, texture::TileConfig tileConfig, texture::TextureType type, u32 baseArrayLayer = 0, u32 layerCount = 1, u32 layerStride = 0, u32 mipLevelCount = 1, u32 viewMipBase = 0, u32 viewMipCount = 1) GuestTexture(span<u8> mapping, texture::Dimensions dimensions, texture::Format format, texture::TileConfig tileConfig, vk::ImageViewType viewType, u32 baseArrayLayer = 0, u32 layerCount = 1, u32 layerStride = 0, u32 mipLevelCount = 1, u32 viewMipBase = 0, u32 viewMipCount = 1)
: mappings(1, mapping), : mappings(1, mapping),
dimensions(dimensions), dimensions(dimensions),
format(format), format(format),
tileConfig(tileConfig), tileConfig(tileConfig),
type(type), viewType(viewType),
baseArrayLayer(baseArrayLayer), baseArrayLayer(baseArrayLayer),
layerCount(layerCount), layerCount(layerCount),
layerStride(layerStride), layerStride(layerStride),
@ -296,6 +272,15 @@ namespace skyline::gpu {
* @return The size of a single layer with layout alignment in bytes * @return The size of a single layer with layout alignment in bytes
*/ */
u32 GetLayerStride(); u32 GetLayerStride();
/**
* @return The most appropriate backing image type for this texture
*/
vk::ImageType GetImageType() const;
u32 GetViewLayerCount() const;
u32 GetViewDepth() const;
}; };
class TextureManager; class TextureManager;

View File

@ -45,13 +45,18 @@ namespace skyline::gpu {
if (firstHostMapping == hostMappings.begin() && firstHostMapping->begin() == guestMapping.begin() && mappingMatch && lastHostMapping == hostMappings.end() && lastGuestMapping.end() == std::prev(lastHostMapping)->end()) { if (firstHostMapping == hostMappings.begin() && firstHostMapping->begin() == guestMapping.begin() && mappingMatch && lastHostMapping == hostMappings.end() && lastGuestMapping.end() == std::prev(lastHostMapping)->end()) {
// We've gotten a perfect 1:1 match for *all* mappings from the start to end, we just need to check for compatibility aside from this // We've gotten a perfect 1:1 match for *all* mappings from the start to end, we just need to check for compatibility aside from this
auto &matchGuestTexture{*hostMapping->texture->guest}; auto &matchGuestTexture{*hostMapping->texture->guest};
if (matchGuestTexture.format->IsCompatible(*guestTexture.format) && (matchGuestTexture.dimensions == guestTexture.dimensions || matchGuestTexture.viewMipBase > 0) && matchGuestTexture.tileConfig == guestTexture.tileConfig) { if (matchGuestTexture.format->IsCompatible(*guestTexture.format) &&
((matchGuestTexture.dimensions.width == guestTexture.dimensions.width &&
matchGuestTexture.dimensions.height == guestTexture.dimensions.height &&
matchGuestTexture.dimensions.depth == guestTexture.GetViewDepth())
|| matchGuestTexture.viewMipBase > 0)
&& matchGuestTexture.tileConfig == guestTexture.tileConfig) {
auto &texture{hostMapping->texture}; auto &texture{hostMapping->texture};
return texture->GetView(static_cast<vk::ImageViewType>(guestTexture.type), vk::ImageSubresourceRange{ return texture->GetView(guestTexture.viewType, vk::ImageSubresourceRange{
.aspectMask = guestTexture.aspect, .aspectMask = guestTexture.aspect,
.baseMipLevel = guestTexture.viewMipBase, .baseMipLevel = guestTexture.viewMipBase,
.levelCount = guestTexture.viewMipCount, .levelCount = guestTexture.viewMipCount,
.layerCount = texture->layerCount, .layerCount = guestTexture.GetViewLayerCount(),
}, guestTexture.format, guestTexture.swizzle); }, guestTexture.format, guestTexture.swizzle);
} }
} /* else if (mappingMatch) { } /* else if (mappingMatch) {
@ -80,11 +85,11 @@ namespace skyline::gpu {
textures.emplace(mapping, TextureMapping{texture, it, guestMapping}); textures.emplace(mapping, TextureMapping{texture, it, guestMapping});
} }
return texture->GetView(static_cast<vk::ImageViewType>(guestTexture.type), vk::ImageSubresourceRange{ return texture->GetView(guestTexture.viewType, vk::ImageSubresourceRange{
.aspectMask = guestTexture.aspect, .aspectMask = guestTexture.aspect,
.baseMipLevel = guestTexture.viewMipBase, .baseMipLevel = guestTexture.viewMipBase,
.levelCount = guestTexture.viewMipCount, .levelCount = guestTexture.viewMipCount,
.layerCount = texture->layerCount, .layerCount = guestTexture.GetViewLayerCount(),
}, guestTexture.format, guestTexture.swizzle); }, guestTexture.format, guestTexture.swizzle);
} }
} }

View File

@ -344,7 +344,7 @@ namespace skyline::service::hosbinder {
} }
gpu::texture::Dimensions dimensions(surface.width, surface.height); gpu::texture::Dimensions dimensions(surface.width, surface.height);
gpu::GuestTexture guestTexture(span<u8>{}, dimensions, format, tileConfig, gpu::texture::TextureType::e2D); gpu::GuestTexture guestTexture(span<u8>{}, dimensions, format, tileConfig, vk::ImageViewType::e2D);
guestTexture.mappings[0] = span<u8>(nvMapHandleObj->GetPointer() + surface.offset, guestTexture.GetLayerStride()); guestTexture.mappings[0] = span<u8>(nvMapHandleObj->GetPointer() + surface.offset, guestTexture.GetLayerStride());
buffer.texture = state.gpu->texture.FindOrCreate(guestTexture)->texture; buffer.texture = state.gpu->texture.FindOrCreate(guestTexture)->texture;
} }