Texture GMMU Address Resolution + Refactor Maxwell3D::CallMethod

Fixes bugs with the Texture Manager lookup, fixes `RenderTarget` address extraction (`low`/`high` were flipped prior), refactors `Maxwell3D::CallMethod` to utilize a case for the register being modified + preventing redundant method calls when no new value is being written to the register, and fixes the behavior of shadow RAM which was broken previously and would lead to incorrect arguments being utilized for methods.
This commit is contained in:
PixelyIon
2021-08-25 00:49:03 +05:30
parent 270f2db1d2
commit 54908afc44
16 changed files with 199 additions and 142 deletions

View File

@ -6,6 +6,7 @@
#include <vulkan/vulkan_raii.hpp>
#include <gpu.h>
#include <gpu/texture/format.h>
#include <soc/gm20b/gmmu.h>
#include <soc/gm20b/engines/maxwell/types.h>
namespace skyline::gpu::context {
@ -18,18 +19,23 @@ namespace skyline::gpu::context {
class GraphicsContext {
private:
GPU &gpu;
soc::gm20b::GMMU &gmmu;
struct RenderTarget {
bool disabled{}; //!< If this RT has been disabled and will be an unbound attachment instead
union {
u64 gpuAddress;
struct {
u32 gpuAddressHigh;
u32 gpuAddressLow;
u32 gpuAddressHigh;
};
};
GuestTexture guest;
std::optional<TextureView> view;
RenderTarget() {
guest.dimensions = texture::Dimensions(1, 1, 1); // We want the depth to be 1 by default (It cannot be set by the application)
}
};
std::array<RenderTarget, maxwell3d::RenderTargetCount> renderTargets{}; //!< The target textures to render into as color attachments
@ -44,7 +50,7 @@ namespace skyline::gpu::context {
public:
GraphicsContext(GPU &gpu) : gpu(gpu) {
GraphicsContext(GPU &gpu, soc::gm20b::GMMU &gmmu) : gpu(gpu), gmmu(gmmu) {
scissors.fill(DefaultScissor);
}
@ -64,19 +70,19 @@ namespace skyline::gpu::context {
renderTarget.view.reset();
}
void SetRenderTargetAddressWidth(size_t index, u32 value) {
void SetRenderTargetWidth(size_t index, u32 value) {
auto &renderTarget{renderTargets.at(index)};
renderTarget.guest.dimensions.width = value;
renderTarget.view.reset();
}
void SetRenderTargetAddressHeight(size_t index, u32 value) {
void SetRenderTargetHeight(size_t index, u32 value) {
auto &renderTarget{renderTargets.at(index)};
renderTarget.guest.dimensions.height = value;
renderTarget.view.reset();
}
void SetRenderTargetAddressFormat(size_t index, maxwell3d::RenderTarget::ColorFormat format) {
void SetRenderTargetFormat(size_t index, maxwell3d::RenderTarget::ColorFormat format) {
auto &renderTarget{renderTargets.at(index)};
renderTarget.guest.format = [&]() -> texture::Format {
switch (format) {
@ -137,8 +143,9 @@ namespace skyline::gpu::context {
return &*renderTarget.view;
if (renderTarget.guest.mappings.empty()) {
// TODO: Fill in mappings
return nullptr;
auto size{std::max<u64>(renderTarget.guest.layerStride * (renderTarget.guest.layerCount - renderTarget.guest.baseArrayLayer), renderTarget.guest.format->GetSize(renderTarget.guest.dimensions))};
auto mappings{gmmu.TranslateRange(renderTarget.gpuAddress, size)};
renderTarget.guest.mappings.assign(mappings.begin(), mappings.end());
}
return &*(renderTarget.view = gpu.texture.FindOrCreate(renderTarget.guest));

View File

@ -111,7 +111,6 @@ namespace skyline::gpu::memory {
Image MemoryManager::AllocateMappedImage(const vk::ImageCreateInfo &createInfo) {
VmaAllocationCreateInfo allocationCreateInfo{
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT,
.usage = VMA_MEMORY_USAGE_UNKNOWN,
.memoryTypeBits = static_cast<u32>(vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent | vk::MemoryPropertyFlagBits::eDeviceLocal),
};

View File

@ -31,6 +31,7 @@ namespace skyline::gpu::memory {
/**
* @brief A Vulkan image which VMA allocates and manages the backing memory for
* @note Any images created with VMA_ALLOCATION_CREATE_MAPPED_BIT must not be utilized with this since it'll unconditionally unmap when a pointer is present which is illegal when an image was created with that flag as unmapping will be automatically performed on image deletion
*/
struct Image {
private:

View File

@ -21,23 +21,26 @@ namespace skyline::gpu {
// 4.2) If they aren't, we delete them from the map
// 5) Create a new texture and insert it in the map then return it
std::scoped_lock lock(mutex);
std::shared_ptr<Texture> match{};
auto mappingEnd{std::upper_bound(textures.begin(), textures.end(), guestMapping)}, hostMapping{mappingEnd};
while (hostMapping != textures.begin() && std::prev(hostMapping)->end() > guestMapping.begin()) {
while (hostMapping != textures.begin() && (--hostMapping)->end() > guestMapping.begin()) {
auto &hostMappings{hostMapping->texture->guest->mappings};
if (!hostMapping->contains(guestMapping))
continue;
// We need to check that all corresponding mappings in the candidate texture and the guest texture match up
// Only the start of the first matched mapping and the end of the last mapping can not match up as this is the case for views
auto firstHostMapping{hostMapping->iterator};
auto lastGuestMapping{guestTexture.mappings.back()};
auto lastHostMapping{std::find_if(firstHostMapping, hostMappings.end(), [&lastGuestMapping](const span<u8> &it) {
return lastGuestMapping.begin() >= it.begin() && lastGuestMapping.size() <= it.size();
})};
return lastGuestMapping.begin() > it.begin() && lastGuestMapping.end() > it.end();
})}; //!< A past-the-end iterator for the last host mapping, the final valid mapping is prior to this iterator
bool mappingMatch{std::equal(firstHostMapping, lastHostMapping, guestTexture.mappings.begin(), guestTexture.mappings.end(), [](const span<u8> &lhs, const span<u8> &rhs) {
return lhs.end() == rhs.end(); // We check end() here to implicitly ignore any offset from the first mapping
})};
if (firstHostMapping == hostMappings.begin() && firstHostMapping->begin() == guestMapping.begin() && mappingMatch && lastHostMapping == std::prev(hostMappings.end()) && lastGuestMapping.end() == 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
auto &matchGuestTexture{*hostMapping->texture->guest};
if (matchGuestTexture.format->IsCompatible(*guestTexture.format) && matchGuestTexture.dimensions == guestTexture.dimensions && matchGuestTexture.tileConfig == guestTexture.tileConfig) {

View File

@ -27,10 +27,6 @@ namespace skyline::gpu {
std::mutex mutex; //!< Synchronizes access to the texture mappings
std::vector<TextureMapping> textures; //!< A sorted vector of all texture mappings
bool IsSizeCompatible(texture::Dimensions lhsDimension, texture::TileConfig lhsConfig, texture::Dimensions rhsDimension, texture::TileConfig rhsConfig) {
return lhsDimension == rhsDimension && lhsConfig == rhsConfig;
}
public:
TextureManager(GPU &gpu);