mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-10-18 08:26:57 -04:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b7570b24d | |||
| de7e6e01d4 | |||
| e0d47f2790 |
@@ -24,7 +24,7 @@ else()
|
||||
endif()
|
||||
|
||||
if (lz4_FOUND AND NOT TARGET lz4::lz4)
|
||||
if (TARGET LZ4::lz4_shared)
|
||||
if (TARGET LZ4::lz4_shared AND NOT YUZU_STATIC_BUILD)
|
||||
add_library(lz4::lz4 ALIAS LZ4::lz4_shared)
|
||||
elseif (TARGET LZ4::lz4_static)
|
||||
add_library(lz4::lz4 ALIAS LZ4::lz4_static)
|
||||
|
||||
@@ -40,12 +40,6 @@ bool IsConnectionBased(Type type) {
|
||||
}
|
||||
}
|
||||
|
||||
// Size of a real BSD sockaddr_in. The guest SockAddrIn struct is padded to 0x100 so that longer
|
||||
// input addresses can be read, but an address handed back to the game is always this long.
|
||||
// Reporting 0x100 lets a game that trusts the length copy 240 bytes past its 16-byte sockaddr
|
||||
// (suspected cause of Crysis Remastered crashing ~30 s after its getsockname() call).
|
||||
constexpr size_t GuestSockAddrInSize = 16;
|
||||
|
||||
template <typename T>
|
||||
T GetValue(std::span<const u8> buffer) {
|
||||
T t{};
|
||||
@@ -661,9 +655,6 @@ std::pair<s32, Errno> BSD_USA::AcceptImpl(s32 fd, std::vector<u8>& write_buffer)
|
||||
new_descriptor.is_connection_based = descriptor.is_connection_based;
|
||||
|
||||
const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
|
||||
if (write_buffer.size() > GuestSockAddrInSize) {
|
||||
write_buffer.resize(GuestSockAddrInSize);
|
||||
}
|
||||
PutValue(write_buffer, guest_addr_in);
|
||||
|
||||
return {new_fd, Errno::SUCCESS};
|
||||
@@ -723,8 +714,8 @@ Errno BSD_USA::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||
}
|
||||
const SockAddrIn guest_addrin = Translate(addr_in);
|
||||
|
||||
// The reported length must be that of a real sockaddr_in, not of the padded guest struct.
|
||||
write_buffer.resize(GuestSockAddrInSize);
|
||||
ASSERT(write_buffer.size() >= sizeof(guest_addrin));
|
||||
write_buffer.resize(sizeof(guest_addrin));
|
||||
PutValue(write_buffer, guest_addrin);
|
||||
return Translate(bsd_errno);
|
||||
}
|
||||
@@ -745,8 +736,8 @@ Errno BSD_USA::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||
}
|
||||
const SockAddrIn guest_addrin = Translate(addr_in);
|
||||
|
||||
// The reported length must be that of a real sockaddr_in, not of the padded guest struct.
|
||||
write_buffer.resize(GuestSockAddrInSize);
|
||||
ASSERT(write_buffer.size() >= sizeof(guest_addrin));
|
||||
write_buffer.resize(sizeof(guest_addrin));
|
||||
PutValue(write_buffer, guest_addrin);
|
||||
return Translate(bsd_errno);
|
||||
}
|
||||
@@ -958,9 +949,8 @@ std::pair<s32, Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>&
|
||||
if (ret < 0) {
|
||||
addr.clear();
|
||||
} else {
|
||||
ASSERT(addr.size() >= GuestSockAddrInSize);
|
||||
ASSERT(addr.size() >= 16);
|
||||
const SockAddrIn result = Translate(addr_in);
|
||||
addr.resize(GuestSockAddrInSize);
|
||||
PutValue(addr, result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,21 +83,20 @@ void Joycons::ScanThread(std::stop_token stop_token) {
|
||||
constexpr u16 nintendo_vendor_id = 0x057e;
|
||||
Common::SetCurrentThreadName("JoyconScanThread");
|
||||
|
||||
auto last_ret = 0u;
|
||||
do {
|
||||
auto ret = SDL_hid_device_change_count();
|
||||
if (last_ret == 0u || last_ret != ret) {
|
||||
last_ret = ret;
|
||||
SDL_hid_device_info* devs = SDL_hid_enumerate(nintendo_vendor_id, 0x0);
|
||||
for (auto* cur_dev = devs; cur_dev; cur_dev = cur_dev->next) {
|
||||
if (IsDeviceNew(cur_dev)) {
|
||||
LOG_DEBUG(Input, "Device Found,type : {:04X} {:04X}", cur_dev->vendor_id, cur_dev->product_id);
|
||||
RegisterNewDevice(cur_dev);
|
||||
}
|
||||
cur_dev = cur_dev->next;
|
||||
SDL_hid_device_info* devs = SDL_hid_enumerate(nintendo_vendor_id, 0x0);
|
||||
SDL_hid_device_info* cur_dev = devs;
|
||||
|
||||
while (cur_dev) {
|
||||
if (IsDeviceNew(cur_dev)) {
|
||||
LOG_DEBUG(Input, "Device Found,type : {:04X} {:04X}", cur_dev->vendor_id,
|
||||
cur_dev->product_id);
|
||||
RegisterNewDevice(cur_dev);
|
||||
}
|
||||
SDL_hid_free_enumeration(devs);
|
||||
cur_dev = cur_dev->next;
|
||||
}
|
||||
|
||||
SDL_hid_free_enumeration(devs);
|
||||
} while (Common::StoppableTimedWait(stop_token, std::chrono::seconds{5}));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <queue>
|
||||
@@ -13,9 +17,10 @@
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
template <typename Pred>
|
||||
auto BreadthFirstSearch(const Value& value, Pred&& pred)
|
||||
template <typename Pred, typename Resolve = std::identity>
|
||||
auto BreadthFirstSearch(Value value, Pred&& pred, Resolve&& resolve = {})
|
||||
-> std::invoke_result_t<Pred, const Inst*> {
|
||||
value = resolve(value);
|
||||
if (value.IsImmediate()) {
|
||||
// Nothing to do with immediates
|
||||
return std::nullopt;
|
||||
@@ -36,7 +41,7 @@ auto BreadthFirstSearch(const Value& value, Pred&& pred)
|
||||
}
|
||||
// Visit the right most arguments first
|
||||
for (size_t arg = inst->NumArgs(); arg--;) {
|
||||
const Value arg_value{inst->Arg(arg)};
|
||||
const Value arg_value{resolve(inst->Arg(arg))};
|
||||
if (arg_value.IsImmediate()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,60 @@ struct TextureInst {
|
||||
|
||||
using TextureInstVector = boost::container::small_vector<TextureInst, 24>;
|
||||
|
||||
const IR::Block* ConditionalHeader(const IR::Block* body) {
|
||||
const auto predecessors{body->ImmPredecessors()};
|
||||
const auto successors{body->ImmSuccessors()};
|
||||
if (predecessors.size() != 1 || successors.size() != 1) return nullptr;
|
||||
const IR::Block* const header{predecessors[0]};
|
||||
const auto targets{header->ImmSuccessors()};
|
||||
if (targets.size() != 2 || targets[0] != body || targets[1] != successors[0] ||
|
||||
targets[1]->ImmPredecessors().size() != 2 || header->empty() ||
|
||||
header->back().GetOpcode() != IR::Opcode::ConditionRef) {
|
||||
return nullptr;
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
std::pair<IR::Value, bool> Condition(IR::Value value) {
|
||||
bool negated{};
|
||||
while (const IR::Inst* inst = value.TryInstRecursive()) {
|
||||
if (inst->GetOpcode() == IR::Opcode::LogicalNot) negated = !negated;
|
||||
else if (inst->GetOpcode() != IR::Opcode::ConditionRef) break;
|
||||
value = inst->Arg(0);
|
||||
}
|
||||
return {value.Resolve(), negated};
|
||||
}
|
||||
|
||||
IR::Value ResolveConditionalPhi(IR::Value value, const IR::Block* block) {
|
||||
if (!value.Resolve().IsPhi() || !(block = ConditionalHeader(block))) return value;
|
||||
const auto [condition, negated]{Condition(block->back().Arg(0))};
|
||||
boost::container::small_vector<const IR::Block*, 16> visited;
|
||||
while (value.Resolve().IsPhi() && std::ranges::find(visited, block) == visited.end()) {
|
||||
visited.push_back(block);
|
||||
const IR::Inst* const phi{value.InstRecursive()};
|
||||
const auto predecessors{block->ImmPredecessors()};
|
||||
if (predecessors.size() == 1) {
|
||||
block = predecessors[0];
|
||||
continue;
|
||||
}
|
||||
if (predecessors.size() != 2 || phi->NumArgs() != 2) break;
|
||||
const IR::Block* body{predecessors[0]};
|
||||
const IR::Block* header{ConditionalHeader(body)};
|
||||
if (header != predecessors[1]) {
|
||||
body = predecessors[1];
|
||||
header = ConditionalHeader(body);
|
||||
if (header != predecessors[0]) break;
|
||||
}
|
||||
const auto [other_condition, other_negated]{Condition(header->back().Arg(0))};
|
||||
if (condition == other_condition && (phi->PhiBlock(0) == header || phi->PhiBlock(0) == body)) {
|
||||
const IR::Block* const incoming{negated == other_negated ? body : header};
|
||||
value = phi->Arg(phi->PhiBlock(0) == incoming ? 0 : 1);
|
||||
}
|
||||
block = header;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
constexpr u32 DESCRIPTOR_SIZE = 8;
|
||||
constexpr u32 DESCRIPTOR_SIZE_SHIFT = u32(std::countr_zero(DESCRIPTOR_SIZE));
|
||||
constexpr u32 DESCRIPTOR_MAX_COUNT = 1024;
|
||||
@@ -299,23 +353,23 @@ static inline bool IsTexturePixelFormatIntegerCached(Environment& env,
|
||||
}
|
||||
|
||||
|
||||
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info);
|
||||
static inline std::optional<ConstBufferAddr> TrackCached(const IR::Value& v, Environment& env, const HostTranslateInfo& host_info) {
|
||||
if (const IR::Inst* key = v.InstRecursive()) {
|
||||
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info, const IR::Block* block);
|
||||
static inline std::optional<ConstBufferAddr> TrackCached(const IR::Value& v, Environment& env, const HostTranslateInfo& host_info, const IR::Block* block) {
|
||||
if (const IR::Inst* key = v.TryInstRecursive()) {
|
||||
if (auto it = env.track_cache.find(key); it != env.track_cache.end()) return it->second;
|
||||
auto found = Track(v, env, host_info);
|
||||
auto found = Track(v, env, host_info, block);
|
||||
if (found) env.track_cache.emplace(key, *found);
|
||||
return found;
|
||||
}
|
||||
return Track(v, env, host_info);
|
||||
return Track(v, env, host_info, block);
|
||||
}
|
||||
|
||||
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info);
|
||||
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info, const IR::Block* block);
|
||||
|
||||
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info) {
|
||||
return IR::BreadthFirstSearch(value, [&env, &host_info](const IR::Inst* inst) {
|
||||
return TryGetConstBuffer(inst, env, host_info);
|
||||
});
|
||||
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info, const IR::Block* block) {
|
||||
return IR::BreadthFirstSearch(value, [&env, &host_info, block](const IR::Inst* inst) {
|
||||
return TryGetConstBuffer(inst, env, host_info, block);
|
||||
}, [&](IR::Value operand) { return ResolveConditionalPhi(operand, block); });
|
||||
}
|
||||
|
||||
std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) {
|
||||
@@ -339,13 +393,13 @@ std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) {
|
||||
return ReadCbufCached(env, index_number, offset_number);
|
||||
}
|
||||
|
||||
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info) {
|
||||
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info, const IR::Block* block) {
|
||||
switch (inst->GetOpcode()) {
|
||||
default:
|
||||
return std::nullopt;
|
||||
case IR::Opcode::BitwiseOr32: {
|
||||
std::optional lhs{TrackCached(inst->Arg(0), env, host_info)};
|
||||
std::optional rhs{TrackCached(inst->Arg(1), env, host_info)};
|
||||
std::optional lhs{TrackCached(inst->Arg(0), env, host_info, block)};
|
||||
std::optional rhs{TrackCached(inst->Arg(1), env, host_info, block)};
|
||||
if (!lhs || !rhs) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -375,7 +429,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
|
||||
if (!shift.IsImmediate()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional lhs{TrackCached(inst->Arg(0), env, host_info)};
|
||||
std::optional lhs{TrackCached(inst->Arg(0), env, host_info, block)};
|
||||
if (lhs) {
|
||||
lhs->shift_left = shift.U32();
|
||||
}
|
||||
@@ -403,7 +457,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
|
||||
return std::nullopt;
|
||||
} while (false);
|
||||
}
|
||||
std::optional lhs{TrackCached(op1, env, host_info)};
|
||||
std::optional lhs{TrackCached(op1, env, host_info, block)};
|
||||
if (lhs) {
|
||||
lhs->shift_left = static_cast<u32>(std::countr_zero(op2.U32()));
|
||||
}
|
||||
@@ -469,7 +523,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
|
||||
TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst, const HostTranslateInfo& host_info) {
|
||||
ConstBufferAddr addr;
|
||||
if (IsBindless(inst)) {
|
||||
const std::optional<ConstBufferAddr> track_addr{TrackCached(inst.Arg(0), env, host_info)};
|
||||
const std::optional<ConstBufferAddr> track_addr{TrackCached(inst.Arg(0), env, host_info, block)};
|
||||
|
||||
if (!track_addr) {
|
||||
throw NotImplementedException("Failed to track bindless texture constant buffer");
|
||||
@@ -671,6 +725,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||
env.track_cache.clear();
|
||||
TextureInstVector to_replace;
|
||||
for (IR::Block* const block : program.post_order_blocks) {
|
||||
env.track_cache.clear();
|
||||
for (IR::Inst& inst : block->Instructions()) {
|
||||
if (!IsTextureInstruction(inst)) {
|
||||
continue;
|
||||
|
||||
@@ -563,8 +563,10 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
|
||||
});
|
||||
}
|
||||
|
||||
void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass,
|
||||
VkFramebuffer framebuffer_handle, VkExtent2D render_area) {
|
||||
void BeginRenderPass(vk::CommandBuffer& cmdbuf, const Framebuffer* framebuffer) {
|
||||
const VkRenderPass render_pass = framebuffer->RenderPass();
|
||||
const VkFramebuffer framebuffer_handle = framebuffer->Handle();
|
||||
const VkExtent2D render_area = framebuffer->RenderArea();
|
||||
const VkRenderPassBeginInfo renderpass_bi{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.pNext = nullptr,
|
||||
@@ -677,67 +679,27 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, const ImageV
|
||||
void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view,
|
||||
VkImage src_image, VkSampler src_sampler,
|
||||
const Region2D& dst_region, const Region2D& src_region,
|
||||
const Extent3D& src_size,
|
||||
const std::optional<VkRect2D>& scissor) {
|
||||
const Extent3D& src_size) {
|
||||
const BlitImagePipelineKey key{
|
||||
.renderpass = dst_framebuffer->RenderPass(),
|
||||
.operation = Tegra::Engines::Fermi2D::Operation::SrcCopy,
|
||||
};
|
||||
const VkPipelineLayout layout = *one_texture_pipeline_layout;
|
||||
const VkPipeline pipeline = FindOrEmplaceColorPipeline(key);
|
||||
|
||||
// Clip the destination rectangle to the scissor; the viewport keeps the full rectangle so
|
||||
// the texture coordinates are unchanged.
|
||||
std::optional<VkRect2D> clip;
|
||||
if (scissor) {
|
||||
const s64 x0 = (std::max)(s64{(std::min)(dst_region.start.x, dst_region.end.x)},
|
||||
s64{scissor->offset.x});
|
||||
const s64 y0 = (std::max)(s64{(std::min)(dst_region.start.y, dst_region.end.y)},
|
||||
s64{scissor->offset.y});
|
||||
const s64 x1 = (std::min)(s64{(std::max)(dst_region.start.x, dst_region.end.x)},
|
||||
s64{scissor->offset.x} + scissor->extent.width);
|
||||
const s64 y1 = (std::min)(s64{(std::max)(dst_region.start.y, dst_region.end.y)},
|
||||
s64{scissor->offset.y} + scissor->extent.height);
|
||||
if (x1 <= x0 || y1 <= y0) {
|
||||
return; // Fully scissored out.
|
||||
}
|
||||
clip = VkRect2D{
|
||||
.offset = {static_cast<s32>(x0), static_cast<s32>(y0)},
|
||||
.extent = {static_cast<u32>(x1 - x0), static_cast<u32>(y1 - y0)},
|
||||
};
|
||||
}
|
||||
|
||||
// Read the framebuffer now: by the time the worker runs this, the texture cache may have
|
||||
// retired it (moved into its delayed-destruction ring), leaving a null handle behind - Turnip
|
||||
// then crashed in vkCmdBeginRenderPass (Crysis Remastered, DrawTexture).
|
||||
const VkRenderPass render_pass = dst_framebuffer->RenderPass();
|
||||
const VkFramebuffer framebuffer_handle = dst_framebuffer->Handle();
|
||||
const VkExtent2D render_area = dst_framebuffer->RenderArea();
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
scheduler.Record([this, render_pass, framebuffer_handle, render_area, src_image_view,
|
||||
src_image, src_sampler, dst_region, src_region, src_size, pipeline, layout,
|
||||
clip](vk::CommandBuffer cmdbuf) {
|
||||
// Images stay in GENERAL everywhere (the descriptor below says so too): only make the
|
||||
// earlier writes visible. Moving the source to SHADER_READ_ONLY_OPTIMAL left it there
|
||||
// for good, so every later render pass used it in the wrong layout.
|
||||
TransitionImageLayout(cmdbuf, src_image, VK_IMAGE_LAYOUT_GENERAL);
|
||||
BeginRenderPass(cmdbuf, render_pass, framebuffer_handle, render_area);
|
||||
scheduler.Record([this, dst_framebuffer, src_image_view, src_image, src_sampler, dst_region,
|
||||
src_region, src_size, pipeline, layout](vk::CommandBuffer cmdbuf) {
|
||||
TransitionImageLayout(cmdbuf, src_image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
BeginRenderPass(cmdbuf, dst_framebuffer);
|
||||
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
|
||||
UpdateOneTextureDescriptorSet(device, descriptor_set, src_sampler, src_image_view);
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
|
||||
nullptr);
|
||||
BindBlitState(cmdbuf, layout, dst_region, src_region, src_size);
|
||||
if (clip) {
|
||||
cmdbuf.SetScissor(0, *clip);
|
||||
}
|
||||
cmdbuf.Draw(3, 1, 0, 0);
|
||||
cmdbuf.EndRenderPass();
|
||||
});
|
||||
// The blit set its own pipeline, viewport and scissor: make the next draw set its own again,
|
||||
// as every other helper here does. Without this the next draw kept the blit's viewport and
|
||||
// scissor: Crysis Remastered drew its 256x256 minimap map over the whole screen.
|
||||
scheduler.InvalidateState();
|
||||
}
|
||||
|
||||
void BlitImageHelper::BlitImpl(const Framebuffer* dst_framebuffer,
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
@@ -79,12 +78,9 @@ public:
|
||||
Tegra::Engines::Fermi2D::Filter filter,
|
||||
Tegra::Engines::Fermi2D::Operation operation);
|
||||
|
||||
/// @param scissor Optional clip rectangle in framebuffer pixels (DrawTexture honours the
|
||||
/// guest scissor like any other draw).
|
||||
void BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view,
|
||||
VkImage src_image, VkSampler src_sampler, const Region2D& dst_region,
|
||||
const Region2D& src_region, const Extent3D& src_size,
|
||||
const std::optional<VkRect2D>& scissor = std::nullopt);
|
||||
const Region2D& src_region, const Extent3D& src_size);
|
||||
|
||||
void BlitColorMSAA(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
|
||||
const Region2D& dst_region, const Region2D& src_region);
|
||||
|
||||
@@ -393,35 +393,8 @@ void RasterizerVulkan::DrawTexture() {
|
||||
.y = ScaleSrc(draw_texture_state.src_y1)}};
|
||||
Extent3D src_size = {static_cast<u32>(ScaleSrc(texture.size.width)),
|
||||
static_cast<u32>(ScaleSrc(texture.size.height)), texture.size.depth};
|
||||
// DrawTexture rectangles go through the fragment pipeline, scissor included.
|
||||
std::optional<VkRect2D> scissor;
|
||||
if (maxwell3d->regs.scissor_test[0].enable) {
|
||||
u32 up_scale = 1;
|
||||
u32 down_shift = 0;
|
||||
if (dst_rescaling) {
|
||||
up_scale = Settings::values.resolution_info.up_scale;
|
||||
down_shift = Settings::values.resolution_info.down_shift;
|
||||
}
|
||||
scissor = GetScissorState(maxwell3d->regs, 0, up_scale, down_shift);
|
||||
}
|
||||
|
||||
// Drawing a texture 1:1 onto itself without blending changes nothing on hardware, but here
|
||||
// it samples the image the render pass is writing to: undefined in Vulkan, and on tiled GPUs
|
||||
// it reads whatever the image memory last held. Crysis Remastered does this every frame for
|
||||
// its post-processing buffers (suspected cause of its map covering the whole screen).
|
||||
const auto& fb_images = framebuffer->Images();
|
||||
const bool src_is_target =
|
||||
std::find(fb_images.begin(), fb_images.begin() + framebuffer->NumImages(),
|
||||
texture.ImageHandle()) != fb_images.begin() + framebuffer->NumImages();
|
||||
if (src_is_target && src_rescaling == dst_rescaling &&
|
||||
src_region.start.x == dst_region.start.x && src_region.start.y == dst_region.start.y &&
|
||||
src_region.end.x == dst_region.end.x && src_region.end.y == dst_region.end.y &&
|
||||
maxwell3d->regs.blend.enable[0] == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
blit_image.BlitColor(framebuffer, texture.RenderTarget(), texture.ImageHandle(),
|
||||
sampler->Handle(), dst_region, src_region, src_size, scissor);
|
||||
sampler->Handle(), dst_region, src_region, src_size);
|
||||
}
|
||||
|
||||
void RasterizerVulkan::Clear(u32 layer_count) {
|
||||
|
||||
Reference in New Issue
Block a user