Merge branch 'master' into vulkan
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
<asmv3:application>
|
||||
<asmv3:windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitor</dpiAwareness>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
</asmv3:windowsSettings>
|
||||
</asmv3:application>
|
||||
</assembly>
|
||||
|
||||
@@ -103,22 +103,6 @@ void D3D12CommandProcessor::RestoreEdramSnapshot(const void* snapshot) {
|
||||
render_target_cache_->RestoreEdramSnapshot(snapshot);
|
||||
}
|
||||
|
||||
uint32_t D3D12CommandProcessor::GetCurrentColorMask(
|
||||
uint32_t shader_writes_color_targets) const {
|
||||
auto& regs = *register_file_;
|
||||
if (regs.Get<reg::RB_MODECONTROL>().edram_mode !=
|
||||
xenos::ModeControl::kColorDepth) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t color_mask = regs[XE_GPU_REG_RB_COLOR_MASK].u32 & 0xFFFF;
|
||||
for (uint32_t i = 0; i < 4; ++i) {
|
||||
if (!(shader_writes_color_targets & (1 << i))) {
|
||||
color_mask &= ~(0xF << (i * 4));
|
||||
}
|
||||
}
|
||||
return color_mask;
|
||||
}
|
||||
|
||||
void D3D12CommandProcessor::PushTransitionBarrier(
|
||||
ID3D12Resource* resource, D3D12_RESOURCE_STATES old_state,
|
||||
D3D12_RESOURCE_STATES new_state, UINT subresource) {
|
||||
@@ -699,7 +683,7 @@ void D3D12CommandProcessor::ReleaseScratchGPUBuffer(
|
||||
void D3D12CommandProcessor::SetExternalPipeline(ID3D12PipelineState* pipeline) {
|
||||
if (current_external_pipeline_ != pipeline) {
|
||||
current_external_pipeline_ = pipeline;
|
||||
current_cached_pipeline_ = nullptr;
|
||||
current_guest_pipeline_ = nullptr;
|
||||
deferred_command_list_.D3DSetPipelineState(pipeline);
|
||||
}
|
||||
}
|
||||
@@ -723,7 +707,7 @@ void D3D12CommandProcessor::SetViewport(const D3D12_VIEWPORT& viewport) {
|
||||
ff_viewport_update_needed_ |= ff_viewport_.MaxDepth != viewport.MaxDepth;
|
||||
if (ff_viewport_update_needed_) {
|
||||
ff_viewport_ = viewport;
|
||||
deferred_command_list_.RSSetViewport(viewport);
|
||||
deferred_command_list_.RSSetViewport(ff_viewport_);
|
||||
ff_viewport_update_needed_ = false;
|
||||
}
|
||||
}
|
||||
@@ -735,7 +719,7 @@ void D3D12CommandProcessor::SetScissorRect(const D3D12_RECT& scissor_rect) {
|
||||
ff_scissor_update_needed_ |= ff_scissor_.bottom != scissor_rect.bottom;
|
||||
if (ff_scissor_update_needed_) {
|
||||
ff_scissor_ = scissor_rect;
|
||||
deferred_command_list_.RSSetScissorRect(scissor_rect);
|
||||
deferred_command_list_.RSSetScissorRect(ff_scissor_);
|
||||
ff_scissor_update_needed_ = false;
|
||||
}
|
||||
}
|
||||
@@ -2152,10 +2136,12 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type,
|
||||
: DxbcShaderTranslator::Modification(0);
|
||||
|
||||
// Set up the render targets - this may perform dispatches and draws.
|
||||
uint32_t pixel_shader_writes_color_targets =
|
||||
pixel_shader ? pixel_shader->writes_color_targets() : 0;
|
||||
uint32_t normalized_color_mask =
|
||||
pixel_shader ? draw_util::GetNormalizedColorMask(
|
||||
regs, pixel_shader->writes_color_targets())
|
||||
: 0;
|
||||
if (!render_target_cache_->Update(is_rasterization_done,
|
||||
pixel_shader_writes_color_targets)) {
|
||||
normalized_color_mask)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2186,7 +2172,8 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type,
|
||||
ID3D12RootSignature* root_signature;
|
||||
if (!pipeline_cache_->ConfigurePipeline(
|
||||
vertex_shader_translation, pixel_shader_translation,
|
||||
primitive_processing_result, bound_depth_and_color_render_target_bits,
|
||||
primitive_processing_result, normalized_color_mask,
|
||||
bound_depth_and_color_render_target_bits,
|
||||
bound_depth_and_color_render_target_formats, &pipeline_handle,
|
||||
&root_signature)) {
|
||||
return false;
|
||||
@@ -2202,10 +2189,10 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type,
|
||||
|
||||
// Bind the pipeline after configuring it and doing everything that may bind
|
||||
// other pipelines.
|
||||
if (current_cached_pipeline_ != pipeline_handle) {
|
||||
if (current_guest_pipeline_ != pipeline_handle) {
|
||||
deferred_command_list_.SetPipelineStateHandle(
|
||||
reinterpret_cast<void*>(pipeline_handle));
|
||||
current_cached_pipeline_ = pipeline_handle;
|
||||
current_guest_pipeline_ = pipeline_handle;
|
||||
current_external_pipeline_ = nullptr;
|
||||
}
|
||||
|
||||
@@ -2241,9 +2228,7 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type,
|
||||
memexport_used, primitive_polygonal,
|
||||
primitive_processing_result.line_loop_closing_index,
|
||||
primitive_processing_result.host_index_endian, viewport_info,
|
||||
used_texture_mask,
|
||||
pixel_shader ? GetCurrentColorMask(pixel_shader->writes_color_targets())
|
||||
: 0);
|
||||
used_texture_mask, normalized_color_mask);
|
||||
|
||||
// Update constant buffers, descriptors and root parameters.
|
||||
if (!UpdateBindings(vertex_shader, pixel_shader, root_signature)) {
|
||||
@@ -2814,7 +2799,7 @@ bool D3D12CommandProcessor::BeginSubmission(bool is_guest_command) {
|
||||
ff_scissor_update_needed_ = true;
|
||||
ff_blend_factor_update_needed_ = true;
|
||||
ff_stencil_ref_update_needed_ = true;
|
||||
current_cached_pipeline_ = nullptr;
|
||||
current_guest_pipeline_ = nullptr;
|
||||
current_external_pipeline_ = nullptr;
|
||||
current_graphics_root_signature_ = nullptr;
|
||||
current_graphics_root_up_to_date_ = 0;
|
||||
@@ -3071,19 +3056,18 @@ void D3D12CommandProcessor::UpdateFixedFunctionState(
|
||||
const RegisterFile& regs = *register_file_;
|
||||
|
||||
// Blend factor.
|
||||
float blend_factor[] = {
|
||||
regs[XE_GPU_REG_RB_BLEND_RED].f32,
|
||||
regs[XE_GPU_REG_RB_BLEND_GREEN].f32,
|
||||
regs[XE_GPU_REG_RB_BLEND_BLUE].f32,
|
||||
regs[XE_GPU_REG_RB_BLEND_ALPHA].f32,
|
||||
};
|
||||
// std::memcmp instead of != so in case of NaN, every draw won't be
|
||||
// invalidating it.
|
||||
ff_blend_factor_update_needed_ |=
|
||||
ff_blend_factor_[0] != regs[XE_GPU_REG_RB_BLEND_RED].f32;
|
||||
ff_blend_factor_update_needed_ |=
|
||||
ff_blend_factor_[1] != regs[XE_GPU_REG_RB_BLEND_GREEN].f32;
|
||||
ff_blend_factor_update_needed_ |=
|
||||
ff_blend_factor_[2] != regs[XE_GPU_REG_RB_BLEND_BLUE].f32;
|
||||
ff_blend_factor_update_needed_ |=
|
||||
ff_blend_factor_[3] != regs[XE_GPU_REG_RB_BLEND_ALPHA].f32;
|
||||
std::memcmp(ff_blend_factor_, blend_factor, sizeof(float) * 4) != 0;
|
||||
if (ff_blend_factor_update_needed_) {
|
||||
ff_blend_factor_[0] = regs[XE_GPU_REG_RB_BLEND_RED].f32;
|
||||
ff_blend_factor_[1] = regs[XE_GPU_REG_RB_BLEND_GREEN].f32;
|
||||
ff_blend_factor_[2] = regs[XE_GPU_REG_RB_BLEND_BLUE].f32;
|
||||
ff_blend_factor_[3] = regs[XE_GPU_REG_RB_BLEND_ALPHA].f32;
|
||||
std::memcpy(ff_blend_factor_, blend_factor, sizeof(float) * 4);
|
||||
deferred_command_list_.D3DOMSetBlendFactor(ff_blend_factor_);
|
||||
ff_blend_factor_update_needed_ = false;
|
||||
}
|
||||
@@ -3104,7 +3088,7 @@ void D3D12CommandProcessor::UpdateFixedFunctionState(
|
||||
ff_stencil_ref_update_needed_ |= ff_stencil_ref_ != stencil_ref;
|
||||
if (ff_stencil_ref_update_needed_) {
|
||||
ff_stencil_ref_ = stencil_ref;
|
||||
deferred_command_list_.D3DOMSetStencilRef(stencil_ref);
|
||||
deferred_command_list_.D3DOMSetStencilRef(ff_stencil_ref_);
|
||||
ff_stencil_ref_update_needed_ = false;
|
||||
}
|
||||
}
|
||||
@@ -3114,7 +3098,7 @@ void D3D12CommandProcessor::UpdateSystemConstantValues(
|
||||
bool shared_memory_is_uav, bool primitive_polygonal,
|
||||
uint32_t line_loop_closing_index, xenos::Endian index_endian,
|
||||
const draw_util::ViewportInfo& viewport_info, uint32_t used_texture_mask,
|
||||
uint32_t color_mask) {
|
||||
uint32_t normalized_color_mask) {
|
||||
#if XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
@@ -3161,7 +3145,7 @@ void D3D12CommandProcessor::UpdateSystemConstantValues(
|
||||
// Get the mask for keeping previous color's components unmodified,
|
||||
// or two UINT32_MAX if no colors actually existing in the RT are written.
|
||||
DxbcShaderTranslator::ROV_GetColorFormatSystemConstants(
|
||||
color_info.color_format, (color_mask >> (i * 4)) & 0b1111,
|
||||
color_info.color_format, (normalized_color_mask >> (i * 4)) & 0b1111,
|
||||
rt_clamp[i][0], rt_clamp[i][1], rt_clamp[i][2], rt_clamp[i][3],
|
||||
rt_keep_masks[i][0], rt_keep_masks[i][1]);
|
||||
}
|
||||
|
||||
@@ -83,16 +83,6 @@ class D3D12CommandProcessor : public CommandProcessor {
|
||||
uint64_t GetCurrentFrame() const { return frame_current_; }
|
||||
uint64_t GetCompletedFrame() const { return frame_completed_; }
|
||||
|
||||
// Gets the current color write mask, taking the pixel shader's write mask
|
||||
// into account. If a shader doesn't write to a render target, it shouldn't be
|
||||
// written to and it shouldn't be even bound - otherwise, in 4D5307E6, one
|
||||
// render target is being destroyed by a shader not writing anything, and in
|
||||
// 58410955, the result of clearing the top tile is being ignored because
|
||||
// there are 4 render targets bound with the same EDRAM base (clearly not
|
||||
// correct usage), but the shader only clears 1, and then EDRAM buffer stores
|
||||
// conflict with each other.
|
||||
uint32_t GetCurrentColorMask(uint32_t shader_writes_color_targets) const;
|
||||
|
||||
void PushTransitionBarrier(
|
||||
ID3D12Resource* resource, D3D12_RESOURCE_STATES old_state,
|
||||
D3D12_RESOURCE_STATES new_state,
|
||||
@@ -362,7 +352,7 @@ class D3D12CommandProcessor : public CommandProcessor {
|
||||
xenos::Endian index_endian,
|
||||
const draw_util::ViewportInfo& viewport_info,
|
||||
uint32_t used_texture_mask,
|
||||
uint32_t color_mask);
|
||||
uint32_t normalized_color_mask);
|
||||
bool UpdateBindings(const D3D12Shader* vertex_shader,
|
||||
const D3D12Shader* pixel_shader,
|
||||
ID3D12RootSignature* root_signature);
|
||||
@@ -593,8 +583,8 @@ class D3D12CommandProcessor : public CommandProcessor {
|
||||
// Currently bound pipeline, either a graphics pipeline from the pipeline
|
||||
// cache (with potentially deferred creation - current_external_pipeline_ is
|
||||
// nullptr in this case) or a non-Xenos graphics or compute pipeline
|
||||
// (current_cached_pipeline_ is nullptr in this case).
|
||||
void* current_cached_pipeline_;
|
||||
// (current_guest_pipeline_ is nullptr in this case).
|
||||
void* current_guest_pipeline_;
|
||||
ID3D12PipelineState* current_external_pipeline_;
|
||||
|
||||
// Currently bound graphics root signature.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -934,6 +934,7 @@ bool PipelineCache::ConfigurePipeline(
|
||||
D3D12Shader::D3D12Translation* vertex_shader,
|
||||
D3D12Shader::D3D12Translation* pixel_shader,
|
||||
const PrimitiveProcessor::ProcessingResult& primitive_processing_result,
|
||||
uint32_t normalized_color_mask,
|
||||
uint32_t bound_depth_and_color_render_target_bits,
|
||||
const uint32_t* bound_depth_and_color_render_target_formats,
|
||||
void** pipeline_handle_out, ID3D12RootSignature** root_signature_out) {
|
||||
@@ -1005,7 +1006,7 @@ bool PipelineCache::ConfigurePipeline(
|
||||
PipelineRuntimeDescription runtime_description;
|
||||
if (!GetCurrentStateDescription(
|
||||
vertex_shader, pixel_shader, primitive_processing_result,
|
||||
bound_depth_and_color_render_target_bits,
|
||||
normalized_color_mask, bound_depth_and_color_render_target_bits,
|
||||
bound_depth_and_color_render_target_formats, runtime_description)) {
|
||||
return false;
|
||||
}
|
||||
@@ -1272,6 +1273,7 @@ bool PipelineCache::GetCurrentStateDescription(
|
||||
D3D12Shader::D3D12Translation* vertex_shader,
|
||||
D3D12Shader::D3D12Translation* pixel_shader,
|
||||
const PrimitiveProcessor::ProcessingResult& primitive_processing_result,
|
||||
uint32_t normalized_color_mask,
|
||||
uint32_t bound_depth_and_color_render_target_bits,
|
||||
const uint32_t* bound_depth_and_color_render_target_formats,
|
||||
PipelineRuntimeDescription& runtime_description_out) {
|
||||
@@ -1409,7 +1411,6 @@ bool PipelineCache::GetCurrentStateDescription(
|
||||
// rasterization will be disabled externally, or the draw call will be dropped
|
||||
// early if the vertex shader doesn't export to memory.
|
||||
bool cull_front, cull_back;
|
||||
float poly_offset = 0.0f, poly_offset_scale = 0.0f;
|
||||
if (primitive_polygonal) {
|
||||
description_out.front_counter_clockwise = pa_su_sc_mode_cntl.face == 0;
|
||||
cull_front = pa_su_sc_mode_cntl.cull_front != 0;
|
||||
@@ -1433,10 +1434,6 @@ bool PipelineCache::GetCurrentStateDescription(
|
||||
xenos::PolygonType::kTriangles) {
|
||||
description_out.fill_mode_wireframe = 1;
|
||||
}
|
||||
if (!edram_rov_used && pa_su_sc_mode_cntl.poly_offset_front_enable) {
|
||||
poly_offset = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_OFFSET].f32;
|
||||
poly_offset_scale = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_SCALE].f32;
|
||||
}
|
||||
}
|
||||
if (!cull_back) {
|
||||
// Back faces aren't culled.
|
||||
@@ -1444,13 +1441,6 @@ bool PipelineCache::GetCurrentStateDescription(
|
||||
xenos::PolygonType::kTriangles) {
|
||||
description_out.fill_mode_wireframe = 1;
|
||||
}
|
||||
// Prefer front depth bias because in general, front faces are the ones
|
||||
// that are rendered (except for shadow volumes).
|
||||
if (!edram_rov_used && pa_su_sc_mode_cntl.poly_offset_back_enable &&
|
||||
poly_offset == 0.0f && poly_offset_scale == 0.0f) {
|
||||
poly_offset = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_BACK_OFFSET].f32;
|
||||
poly_offset_scale = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_BACK_SCALE].f32;
|
||||
}
|
||||
}
|
||||
if (pa_su_sc_mode_cntl.poly_mode != xenos::PolygonModeEnable::kDualMode) {
|
||||
description_out.fill_mode_wireframe = 0;
|
||||
@@ -1459,24 +1449,23 @@ bool PipelineCache::GetCurrentStateDescription(
|
||||
// Filled front faces only, without culling.
|
||||
cull_front = false;
|
||||
cull_back = false;
|
||||
if (!edram_rov_used && pa_su_sc_mode_cntl.poly_offset_para_enable) {
|
||||
poly_offset = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_OFFSET].f32;
|
||||
poly_offset_scale = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_SCALE].f32;
|
||||
}
|
||||
}
|
||||
if (!edram_rov_used) {
|
||||
float poly_offset_host_scale = draw_util::GetD3D10PolygonOffsetFactor(
|
||||
float polygon_offset, polygon_offset_scale;
|
||||
draw_util::GetPreferredFacePolygonOffset(
|
||||
regs, primitive_polygonal, polygon_offset_scale, polygon_offset);
|
||||
float polygon_offset_host_scale = draw_util::GetD3D10PolygonOffsetFactor(
|
||||
regs.Get<reg::RB_DEPTH_INFO>().depth_format, true);
|
||||
// Using ceil here just in case a game wants the offset but passes a value
|
||||
// that is too small - it's better to apply more offset than to make depth
|
||||
// fighting worse or to disable the offset completely (Direct3D 12 takes an
|
||||
// integer value).
|
||||
description_out.depth_bias =
|
||||
int32_t(std::ceil(std::abs(poly_offset * poly_offset_host_scale))) *
|
||||
(poly_offset < 0.0f ? -1 : 1);
|
||||
// "slope computed in subpixels ([...] 1/16)" - R5xx Acceleration.
|
||||
int32_t(
|
||||
std::ceil(std::abs(polygon_offset * polygon_offset_host_scale))) *
|
||||
(polygon_offset < 0.0f ? -1 : 1);
|
||||
description_out.depth_bias_slope_scaled =
|
||||
poly_offset_scale * xenos::kPolygonOffsetScaleSubpixelUnit;
|
||||
polygon_offset_scale * xenos::kPolygonOffsetScaleSubpixelUnit;
|
||||
}
|
||||
if (tessellated && cvars::d3d12_tessellation_wireframe) {
|
||||
description_out.fill_mode_wireframe = 1;
|
||||
@@ -1547,10 +1536,6 @@ bool PipelineCache::GetCurrentStateDescription(
|
||||
|
||||
// Render targets and blending state. 32 because of 0x1F mask, for safety
|
||||
// (all unknown to zero).
|
||||
uint32_t color_mask =
|
||||
pixel_shader ? command_processor_.GetCurrentColorMask(
|
||||
pixel_shader->shader().writes_color_targets())
|
||||
: 0;
|
||||
static const PipelineBlendFactor kBlendFactorMap[32] = {
|
||||
/* 0 */ PipelineBlendFactor::kZero,
|
||||
/* 1 */ PipelineBlendFactor::kOne,
|
||||
@@ -1622,8 +1607,7 @@ bool PipelineCache::GetCurrentStateDescription(
|
||||
reg::RB_COLOR_INFO::rt_register_indices[i]);
|
||||
rt.format = xenos::ColorRenderTargetFormat(
|
||||
bound_depth_and_color_render_target_formats[1 + i]);
|
||||
// TODO(Triang3l): Normalize unused bits of the color write mask.
|
||||
rt.write_mask = (color_mask >> (i * 4)) & 0xF;
|
||||
rt.write_mask = (normalized_color_mask >> (i * 4)) & 0xF;
|
||||
if (rt.write_mask) {
|
||||
auto blendcontrol = regs.Get<reg::RB_BLENDCONTROL>(
|
||||
reg::RB_BLENDCONTROL::rt_register_indices[i]);
|
||||
@@ -2017,9 +2001,6 @@ ID3D12PipelineState* PipelineCache::CreateD3D12Pipeline(
|
||||
}
|
||||
D3D12_RENDER_TARGET_BLEND_DESC& blend_desc =
|
||||
state_desc.BlendState.RenderTarget[i];
|
||||
// Treat 1 * src + 0 * dest as disabled blending (there are opaque
|
||||
// surfaces drawn with blending enabled, but it's 1 * src + 0 * dest, in
|
||||
// 415607E6 - GPU performance is better when not blending.
|
||||
if (rt.src_blend != PipelineBlendFactor::kOne ||
|
||||
rt.dest_blend != PipelineBlendFactor::kZero ||
|
||||
rt.blend_op != xenos::BlendOp::kAdd ||
|
||||
|
||||
@@ -82,6 +82,7 @@ class PipelineCache {
|
||||
D3D12Shader::D3D12Translation* vertex_shader,
|
||||
D3D12Shader::D3D12Translation* pixel_shader,
|
||||
const PrimitiveProcessor::ProcessingResult& primitive_processing_result,
|
||||
uint32_t normalized_color_mask,
|
||||
uint32_t bound_depth_and_color_render_target_bits,
|
||||
const uint32_t* bound_depth_and_color_render_targets_formats,
|
||||
void** pipeline_handle_out, ID3D12RootSignature** root_signature_out);
|
||||
@@ -247,6 +248,7 @@ class PipelineCache {
|
||||
D3D12Shader::D3D12Translation* vertex_shader,
|
||||
D3D12Shader::D3D12Translation* pixel_shader,
|
||||
const PrimitiveProcessor::ProcessingResult& primitive_processing_result,
|
||||
uint32_t normalized_color_mask,
|
||||
uint32_t bound_depth_and_color_render_target_bits,
|
||||
const uint32_t* bound_depth_and_color_render_target_formats,
|
||||
PipelineRuntimeDescription& runtime_description_out);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -68,6 +68,36 @@ const int8_t kD3D10StandardSamplePositions2x[2][2] = {{4, 4}, {-4, -4}};
|
||||
const int8_t kD3D10StandardSamplePositions4x[4][2] = {
|
||||
{-2, -6}, {6, -2}, {-6, 2}, {2, 6}};
|
||||
|
||||
void GetPreferredFacePolygonOffset(const RegisterFile& regs,
|
||||
bool primitive_polygonal, float& scale_out,
|
||||
float& offset_out) {
|
||||
float scale = 0.0f, offset = 0.0f;
|
||||
auto pa_su_sc_mode_cntl = regs.Get<reg::PA_SU_SC_MODE_CNTL>();
|
||||
if (primitive_polygonal) {
|
||||
// Prefer the front polygon offset because in general, front faces are the
|
||||
// ones that are rendered (except for shadow volumes).
|
||||
if (pa_su_sc_mode_cntl.poly_offset_front_enable &&
|
||||
!pa_su_sc_mode_cntl.cull_front) {
|
||||
scale = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_SCALE].f32;
|
||||
offset = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_OFFSET].f32;
|
||||
}
|
||||
if (pa_su_sc_mode_cntl.poly_offset_back_enable &&
|
||||
!pa_su_sc_mode_cntl.cull_back && !scale && !offset) {
|
||||
scale = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_BACK_SCALE].f32;
|
||||
offset = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_BACK_OFFSET].f32;
|
||||
}
|
||||
} else {
|
||||
// Non-triangle primitives use the front offset, but it's toggled via
|
||||
// poly_offset_para_enable.
|
||||
if (pa_su_sc_mode_cntl.poly_offset_para_enable) {
|
||||
scale = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_SCALE].f32;
|
||||
offset = regs[XE_GPU_REG_PA_SU_POLY_OFFSET_FRONT_OFFSET].f32;
|
||||
}
|
||||
}
|
||||
scale_out = scale;
|
||||
offset_out = offset;
|
||||
}
|
||||
|
||||
bool IsPixelShaderNeededWithRasterization(const Shader& shader,
|
||||
const RegisterFile& regs) {
|
||||
assert_true(shader.type() == xenos::ShaderType::kPixel);
|
||||
@@ -550,6 +580,49 @@ void GetScissor(const RegisterFile& regs, Scissor& scissor_out,
|
||||
scissor_out.extent[1] = uint32_t(br_y - tl_y);
|
||||
}
|
||||
|
||||
uint32_t GetNormalizedColorMask(const RegisterFile& regs,
|
||||
uint32_t pixel_shader_writes_color_targets) {
|
||||
if (regs.Get<reg::RB_MODECONTROL>().edram_mode !=
|
||||
xenos::ModeControl::kColorDepth) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t normalized_color_mask = 0;
|
||||
uint32_t rb_color_mask = regs[XE_GPU_REG_RB_COLOR_MASK].u32;
|
||||
for (uint32_t i = 0; i < xenos::kMaxColorRenderTargets; ++i) {
|
||||
// Exclude the render targets not statically written to by the pixel shader.
|
||||
// If the shader doesn't write to a render target, it shouldn't be written
|
||||
// to, and no ownership transfers should happen to it on the host even -
|
||||
// otherwise, in 4D5307E6, one render target is being destroyed by a shader
|
||||
// not writing anything, and in 58410955, the result of clearing the top
|
||||
// tile is being ignored because there are 4 render targets bound with the
|
||||
// same EDRAM base (clearly not correct usage), but the shader only clears
|
||||
// 1, and then ownership of EDRAM portions by host render targets is
|
||||
// conflicting.
|
||||
if (!(pixel_shader_writes_color_targets & (uint32_t(1) << i))) {
|
||||
continue;
|
||||
}
|
||||
// Check if any existing component is written to.
|
||||
uint32_t format_component_mask =
|
||||
(uint32_t(1) << xenos::GetColorRenderTargetFormatComponentCount(
|
||||
regs.Get<reg::RB_COLOR_INFO>(
|
||||
reg::RB_COLOR_INFO::rt_register_indices[i])
|
||||
.color_format)) -
|
||||
1;
|
||||
uint32_t rt_write_mask = (rb_color_mask >> (4 * i)) & format_component_mask;
|
||||
if (!rt_write_mask) {
|
||||
continue;
|
||||
}
|
||||
// Mark the non-existent components as written so in the host driver, no
|
||||
// slow path (involving reading and merging components) is taken if the
|
||||
// driver doesn't perform this check internally, and some components are not
|
||||
// included in the mask even though they actually don't exist in the format.
|
||||
rt_write_mask |= 0b1111 & ~format_component_mask;
|
||||
// Add to the normalized mask.
|
||||
normalized_color_mask |= rt_write_mask << (4 * i);
|
||||
}
|
||||
return normalized_color_mask;
|
||||
}
|
||||
|
||||
xenos::CopySampleSelect SanitizeCopySampleSelect(
|
||||
xenos::CopySampleSelect copy_sample_select, xenos::MsaaSamples msaa_samples,
|
||||
bool is_depth) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -122,6 +122,16 @@ constexpr float GetD3D10PolygonOffsetFactor(
|
||||
return float24_as_0_to_0_5 ? kFloat24Scale * 0.5f : kFloat24Scale;
|
||||
}
|
||||
|
||||
// For hosts not supporting separate front and back polygon offsets, returns the
|
||||
// polygon offset for the face which likely needs the offset the most (and that
|
||||
// will not be culled). The values returned will have the units of the original
|
||||
// registers (the scale is for 1/16 subpixels, multiply by
|
||||
// xenos::kPolygonOffsetScaleSubpixelUnit outside if the value for pixels is
|
||||
// needed).
|
||||
void GetPreferredFacePolygonOffset(const RegisterFile& regs,
|
||||
bool primitive_polygonal, float& scale_out,
|
||||
float& offset_out);
|
||||
|
||||
inline bool DoesCoverageDependOnAlpha(reg::RB_COLORCONTROL rb_colorcontrol) {
|
||||
return (rb_colorcontrol.alpha_test_enable &&
|
||||
rb_colorcontrol.alpha_func != xenos::CompareFunction::kAlways) ||
|
||||
@@ -186,6 +196,17 @@ struct Scissor {
|
||||
void GetScissor(const RegisterFile& regs, Scissor& scissor_out,
|
||||
bool clamp_to_surface_pitch = true);
|
||||
|
||||
// Returns the color component write mask for the draw command taking into
|
||||
// account which color targets are written to by the pixel shader, as well as
|
||||
// components that don't exist in the formats of the render targets (render
|
||||
// targets with only non-existent components written are skipped, but
|
||||
// non-existent components are forced to written if some existing components of
|
||||
// the render target are actually used to make sure the host driver doesn't try
|
||||
// to take a slow path involving reading and mixing if there are any disabled
|
||||
// components even if they don't actually exist).
|
||||
uint32_t GetNormalizedColorMask(const RegisterFile& regs,
|
||||
uint32_t pixel_shader_writes_color_targets);
|
||||
|
||||
// Scales, and shift amounts of the upper 32 bits of the 32x32=64-bit
|
||||
// multiplication result, for fast division and multiplication by
|
||||
// EDRAM-tile-related amounts.
|
||||
|
||||
@@ -13,6 +13,7 @@ project("xenia-gpu-null")
|
||||
"xenia-ui-vulkan",
|
||||
"xxhash",
|
||||
})
|
||||
defines({
|
||||
includedirs({
|
||||
project_root.."/third_party/Vulkan-Headers/include",
|
||||
})
|
||||
local_platform_files()
|
||||
|
||||
@@ -15,7 +15,8 @@ project("xenia-gpu")
|
||||
"xenia-ui",
|
||||
"xxhash",
|
||||
})
|
||||
defines({
|
||||
includedirs({
|
||||
project_root.."/third_party/Vulkan-Headers/include",
|
||||
})
|
||||
local_platform_files()
|
||||
|
||||
@@ -33,7 +34,8 @@ project("xenia-gpu-shader-compiler")
|
||||
"xenia-gpu",
|
||||
"xenia-ui-vulkan",
|
||||
})
|
||||
defines({
|
||||
includedirs({
|
||||
project_root.."/third_party/Vulkan-Headers/include",
|
||||
})
|
||||
files({
|
||||
"shader_compiler_main.cc",
|
||||
|
||||
@@ -366,7 +366,7 @@ void RenderTargetCache::ClearCache() {
|
||||
void RenderTargetCache::BeginFrame() { ResetAccumulatedRenderTargets(); }
|
||||
|
||||
bool RenderTargetCache::Update(bool is_rasterization_done,
|
||||
uint32_t shader_writes_color_targets) {
|
||||
uint32_t normalized_color_mask) {
|
||||
const RegisterFile& regs = register_file();
|
||||
bool interlock_barrier_only = GetPath() == Path::kPixelShaderInterlock;
|
||||
|
||||
@@ -419,9 +419,6 @@ bool RenderTargetCache::Update(bool is_rasterization_done,
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t rts_remaining;
|
||||
uint32_t rt_index;
|
||||
|
||||
// Get used render targets.
|
||||
// [0] is depth / stencil where relevant, [1...4] is color.
|
||||
// Depth / stencil testing / writing is before color in the pipeline.
|
||||
@@ -432,7 +429,7 @@ bool RenderTargetCache::Update(bool is_rasterization_done,
|
||||
uint32_t rts_are_64bpp = 0;
|
||||
uint32_t color_rts_are_gamma = 0;
|
||||
if (is_rasterization_done) {
|
||||
auto rb_depthcontrol = regs.Get<reg::RB_DEPTHCONTROL>();
|
||||
auto rb_depthcontrol = draw_util::GetDepthControlForCurrentEdramMode(regs);
|
||||
if (rb_depthcontrol.z_enable || rb_depthcontrol.stencil_enable) {
|
||||
depth_and_color_rts_used_bits |= 1;
|
||||
auto rb_depth_info = regs.Get<reg::RB_DEPTH_INFO>();
|
||||
@@ -445,50 +442,46 @@ bool RenderTargetCache::Update(bool is_rasterization_done,
|
||||
resource_formats[0] =
|
||||
interlock_barrier_only ? 0 : uint32_t(rb_depth_info.depth_format);
|
||||
}
|
||||
if (regs.Get<reg::RB_MODECONTROL>().edram_mode ==
|
||||
xenos::ModeControl::kColorDepth) {
|
||||
uint32_t rb_color_mask = regs[XE_GPU_REG_RB_COLOR_MASK].u32;
|
||||
rts_remaining = shader_writes_color_targets;
|
||||
while (xe::bit_scan_forward(rts_remaining, &rt_index)) {
|
||||
rts_remaining &= ~(uint32_t(1) << rt_index);
|
||||
auto color_info = regs.Get<reg::RB_COLOR_INFO>(
|
||||
reg::RB_COLOR_INFO::rt_register_indices[rt_index]);
|
||||
xenos::ColorRenderTargetFormat color_format =
|
||||
regs.Get<reg::RB_COLOR_INFO>(
|
||||
reg::RB_COLOR_INFO::rt_register_indices[rt_index])
|
||||
.color_format;
|
||||
if ((rb_color_mask >> (rt_index * 4)) &
|
||||
((uint32_t(1) << xenos::GetColorRenderTargetFormatComponentCount(
|
||||
color_format)) -
|
||||
1)) {
|
||||
uint32_t rt_bit_index = 1 + rt_index;
|
||||
depth_and_color_rts_used_bits |= uint32_t(1) << rt_bit_index;
|
||||
edram_bases[rt_bit_index] =
|
||||
std::min(color_info.color_base, xenos::kEdramTileCount);
|
||||
bool is_64bpp = xenos::IsColorRenderTargetFormat64bpp(color_format);
|
||||
if (is_64bpp) {
|
||||
rts_are_64bpp |= uint32_t(1) << rt_bit_index;
|
||||
}
|
||||
if (color_format == xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA) {
|
||||
color_rts_are_gamma |= uint32_t(1) << rt_index;
|
||||
}
|
||||
xenos::ColorRenderTargetFormat color_resource_format;
|
||||
if (interlock_barrier_only) {
|
||||
// Only changes in mapping between coordinates and addresses are
|
||||
// interesting (along with access overlap between draw calls), thus
|
||||
// only pixel size is relevant.
|
||||
color_resource_format =
|
||||
is_64bpp ? xenos::ColorRenderTargetFormat::k_16_16_16_16
|
||||
: xenos::ColorRenderTargetFormat::k_8_8_8_8;
|
||||
} else {
|
||||
color_resource_format = GetColorResourceFormat(
|
||||
xenos::GetStorageColorFormat(color_format));
|
||||
}
|
||||
resource_formats[rt_bit_index] = uint32_t(color_resource_format);
|
||||
}
|
||||
for (uint32_t i = 0; i < xenos::kMaxColorRenderTargets; ++i) {
|
||||
if (!(normalized_color_mask & (uint32_t(0b1111) << (4 * i)))) {
|
||||
continue;
|
||||
}
|
||||
auto color_info = regs.Get<reg::RB_COLOR_INFO>(
|
||||
reg::RB_COLOR_INFO::rt_register_indices[i]);
|
||||
uint32_t rt_bit_index = 1 + i;
|
||||
depth_and_color_rts_used_bits |= uint32_t(1) << rt_bit_index;
|
||||
edram_bases[rt_bit_index] =
|
||||
std::min(color_info.color_base, xenos::kEdramTileCount);
|
||||
xenos::ColorRenderTargetFormat color_format =
|
||||
regs.Get<reg::RB_COLOR_INFO>(
|
||||
reg::RB_COLOR_INFO::rt_register_indices[i])
|
||||
.color_format;
|
||||
bool is_64bpp = xenos::IsColorRenderTargetFormat64bpp(color_format);
|
||||
if (is_64bpp) {
|
||||
rts_are_64bpp |= uint32_t(1) << rt_bit_index;
|
||||
}
|
||||
if (color_format == xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA) {
|
||||
color_rts_are_gamma |= uint32_t(1) << i;
|
||||
}
|
||||
xenos::ColorRenderTargetFormat color_resource_format;
|
||||
if (interlock_barrier_only) {
|
||||
// Only changes in mapping between coordinates and addresses are
|
||||
// interesting (along with access overlap between draw calls), thus only
|
||||
// pixel size is relevant.
|
||||
color_resource_format =
|
||||
is_64bpp ? xenos::ColorRenderTargetFormat::k_16_16_16_16
|
||||
: xenos::ColorRenderTargetFormat::k_8_8_8_8;
|
||||
} else {
|
||||
color_resource_format =
|
||||
GetColorResourceFormat(xenos::GetStorageColorFormat(color_format));
|
||||
}
|
||||
resource_formats[rt_bit_index] = uint32_t(color_resource_format);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t rts_remaining;
|
||||
uint32_t rt_index;
|
||||
|
||||
// Eliminate other bound render targets if their EDRAM base conflicts with
|
||||
// another render target - it's an error in most host implementations to bind
|
||||
// the same render target into multiple slots, also the behavior would be
|
||||
|
||||
@@ -215,7 +215,7 @@ class RenderTargetCache {
|
||||
virtual void BeginFrame();
|
||||
|
||||
virtual bool Update(bool is_rasterization_done,
|
||||
uint32_t shader_writes_color_targets);
|
||||
uint32_t normalized_color_mask);
|
||||
|
||||
// Returns bits where 0 is whether a depth render target is currently bound on
|
||||
// the host and 1... are whether the same applies to color render targets, and
|
||||
|
||||
@@ -14,6 +14,9 @@ project("xenia-gpu-vulkan")
|
||||
"xenia-ui-vulkan",
|
||||
"xxhash",
|
||||
})
|
||||
includedirs({
|
||||
project_root.."/third_party/Vulkan-Headers/include",
|
||||
})
|
||||
local_platform_files()
|
||||
files({
|
||||
"../shaders/bytecode/vulkan_spirv/*.h",
|
||||
|
||||
@@ -30,6 +30,9 @@ project("xenia-hid-demo")
|
||||
"xenia-ui",
|
||||
"xenia-ui-vulkan",
|
||||
})
|
||||
includedirs({
|
||||
project_root.."/third_party/Vulkan-Headers/include",
|
||||
})
|
||||
files({
|
||||
"hid_demo.cc",
|
||||
"../ui/windowed_app_main_"..platform_suffix..".cc",
|
||||
|
||||
@@ -2,18 +2,13 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2021 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/ui/surface_win.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include "xenia/base/logging.h"
|
||||
|
||||
namespace xe {
|
||||
namespace ui {
|
||||
|
||||
@@ -24,9 +19,9 @@ bool Win32HwndSurface::GetSizeImpl(uint32_t& width_out,
|
||||
if (!GetClientRect(hwnd(), &client_rect)) {
|
||||
return false;
|
||||
}
|
||||
width_out = uint32_t(std::max(client_rect.right - client_rect.left, LONG(0)));
|
||||
height_out =
|
||||
uint32_t(std::max(client_rect.bottom - client_rect.top, LONG(0)));
|
||||
// GetClientRect returns a rectangle with 0 origin.
|
||||
width_out = uint32_t(client_rect.right);
|
||||
height_out = uint32_t(client_rect.bottom);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -10,6 +10,9 @@ project("xenia-ui-vulkan")
|
||||
"xenia-base",
|
||||
"xenia-ui",
|
||||
})
|
||||
includedirs({
|
||||
project_root.."/third_party/Vulkan-Headers/include",
|
||||
})
|
||||
local_platform_files()
|
||||
local_platform_files("functions")
|
||||
files({
|
||||
@@ -28,6 +31,9 @@ project("xenia-ui-window-vulkan-demo")
|
||||
"xenia-ui",
|
||||
"xenia-ui-vulkan",
|
||||
})
|
||||
includedirs({
|
||||
project_root.."/third_party/Vulkan-Headers/include",
|
||||
})
|
||||
files({
|
||||
"../window_demo.cc",
|
||||
"vulkan_window_demo.cc",
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
|
||||
#include <cfloat>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
@@ -706,47 +708,64 @@ bool VulkanProvider::Initialize() {
|
||||
}
|
||||
}
|
||||
device_extensions_enabled.clear();
|
||||
// Checking if already enabled as an optimization to do fewer and fewer
|
||||
// string comparisons, as well as to skip adding extensions promoted to the
|
||||
// core to device_extensions_enabled. Adding literals to
|
||||
// device_extensions_enabled for the most C string lifetime safety.
|
||||
static const std::pair<const char*, size_t> kUsedDeviceExtensions[] = {
|
||||
{"VK_EXT_fragment_shader_interlock",
|
||||
offsetof(DeviceExtensions, ext_fragment_shader_interlock)},
|
||||
{"VK_KHR_dedicated_allocation",
|
||||
offsetof(DeviceExtensions, khr_dedicated_allocation)},
|
||||
{"VK_KHR_image_format_list",
|
||||
offsetof(DeviceExtensions, khr_image_format_list)},
|
||||
{"VK_KHR_portability_subset",
|
||||
offsetof(DeviceExtensions, khr_portability_subset)},
|
||||
{"VK_KHR_shader_float_controls",
|
||||
offsetof(DeviceExtensions, khr_shader_float_controls)},
|
||||
{"VK_KHR_spirv_1_4", offsetof(DeviceExtensions, khr_spirv_1_4)},
|
||||
{"VK_KHR_swapchain", offsetof(DeviceExtensions, khr_swapchain)},
|
||||
};
|
||||
for (const VkExtensionProperties& device_extension :
|
||||
device_extension_properties) {
|
||||
const char* device_extension_name = device_extension.extensionName;
|
||||
// Checking if already enabled as an optimization to do fewer and fewer
|
||||
// string comparisons, as well as to skip adding extensions promoted to
|
||||
// the core to device_extensions_enabled. Adding literals to
|
||||
// device_extensions_enabled for the most C string lifetime safety.
|
||||
if (!device_extensions_.ext_fragment_shader_interlock &&
|
||||
!std::strcmp(device_extension_name,
|
||||
"VK_EXT_fragment_shader_interlock")) {
|
||||
device_extensions_enabled.push_back("VK_EXT_fragment_shader_interlock");
|
||||
device_extensions_.ext_fragment_shader_interlock = true;
|
||||
} else if (!device_extensions_.khr_dedicated_allocation &&
|
||||
!std::strcmp(device_extension_name,
|
||||
"VK_KHR_dedicated_allocation")) {
|
||||
device_extensions_enabled.push_back("VK_KHR_dedicated_allocation");
|
||||
device_extensions_.khr_dedicated_allocation = true;
|
||||
} else if (!device_extensions_.khr_image_format_list &&
|
||||
!std::strcmp(device_extension_name,
|
||||
"VK_KHR_image_format_list")) {
|
||||
device_extensions_enabled.push_back("VK_KHR_image_format_list");
|
||||
device_extensions_.khr_image_format_list = true;
|
||||
} else if (!device_extensions_.khr_shader_float_controls &&
|
||||
!std::strcmp(device_extension_name,
|
||||
"VK_KHR_shader_float_controls")) {
|
||||
device_extensions_enabled.push_back("VK_KHR_shader_float_controls");
|
||||
device_extensions_.khr_shader_float_controls = true;
|
||||
} else if (!device_extensions_.khr_spirv_1_4 &&
|
||||
!std::strcmp(device_extension_name, "VK_KHR_spirv_1_4")) {
|
||||
device_extensions_enabled.push_back("VK_KHR_spirv_1_4");
|
||||
device_extensions_.khr_spirv_1_4 = true;
|
||||
} else if (!device_extensions_.khr_swapchain &&
|
||||
!std::strcmp(device_extension_name, "VK_KHR_swapchain")) {
|
||||
device_extensions_enabled.push_back("VK_KHR_swapchain");
|
||||
device_extensions_.khr_swapchain = true;
|
||||
for (const std::pair<const char*, size_t>& used_device_extension :
|
||||
kUsedDeviceExtensions) {
|
||||
bool& device_extension_flag = *reinterpret_cast<bool*>(
|
||||
reinterpret_cast<char*>(&device_extensions_) +
|
||||
used_device_extension.second);
|
||||
if (!device_extension_flag &&
|
||||
!std::strcmp(device_extension.extensionName,
|
||||
used_device_extension.first)) {
|
||||
device_extensions_enabled.push_back(used_device_extension.first);
|
||||
device_extension_flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_surface_required_ && !device_extensions_.khr_swapchain) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get portability subset features.
|
||||
// VK_KHR_portability_subset reduces, not increases, the capabilities, skip
|
||||
// the device completely if there's no way to retrieve what is actually
|
||||
// unsupported. Though VK_KHR_portability_subset requires
|
||||
// VK_KHR_get_physical_device_properties2, check just in case of an
|
||||
// untrustworthy driver.
|
||||
if (device_extensions_.khr_portability_subset) {
|
||||
if (!instance_extensions_.khr_get_physical_device_properties2) {
|
||||
continue;
|
||||
}
|
||||
device_portability_subset_features_.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_PROPERTIES_KHR;
|
||||
device_portability_subset_features_.pNext = nullptr;
|
||||
VkPhysicalDeviceProperties2KHR device_properties_2;
|
||||
device_properties_2.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
|
||||
device_properties_2.pNext = &device_portability_subset_features_;
|
||||
ifn_.vkGetPhysicalDeviceProperties2KHR(physical_device_,
|
||||
&device_properties_2);
|
||||
}
|
||||
|
||||
// Get the memory types.
|
||||
VkPhysicalDeviceMemoryProperties memory_properties;
|
||||
ifn_.vkGetPhysicalDeviceMemoryProperties(physical_device_current,
|
||||
@@ -843,6 +862,7 @@ bool VulkanProvider::Initialize() {
|
||||
VkDeviceCreateInfo device_create_info;
|
||||
device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||
device_create_info.pNext = nullptr;
|
||||
VkDeviceCreateInfo* device_create_info_last = &device_create_info;
|
||||
device_create_info.flags = 0;
|
||||
device_create_info.queueCreateInfoCount = uint32_t(queue_create_infos.size());
|
||||
device_create_info.pQueueCreateInfos = queue_create_infos.data();
|
||||
@@ -854,6 +874,13 @@ bool VulkanProvider::Initialize() {
|
||||
device_create_info.ppEnabledExtensionNames = device_extensions_enabled.data();
|
||||
// TODO(Triang3l): Enable only needed features.
|
||||
device_create_info.pEnabledFeatures = &device_features_;
|
||||
if (device_extensions_.khr_portability_subset) {
|
||||
// TODO(Triang3l): Enable only needed portability subset features.
|
||||
device_portability_subset_features_.pNext = nullptr;
|
||||
device_create_info_last->pNext = &device_portability_subset_features_;
|
||||
device_create_info_last = reinterpret_cast<VkDeviceCreateInfo*>(
|
||||
&device_portability_subset_features_);
|
||||
}
|
||||
if (ifn_.vkCreateDevice(physical_device_, &device_create_info, nullptr,
|
||||
&device_) != VK_SUCCESS) {
|
||||
XELOGE("Failed to create a Vulkan device");
|
||||
@@ -923,6 +950,34 @@ bool VulkanProvider::Initialize() {
|
||||
device_extensions_.khr_dedicated_allocation ? "yes" : "no");
|
||||
XELOGVK("* VK_KHR_image_format_list: {}",
|
||||
device_extensions_.khr_image_format_list ? "yes" : "no");
|
||||
XELOGVK("* VK_KHR_portability_subset: {}",
|
||||
device_extensions_.khr_portability_subset ? "yes" : "no");
|
||||
if (device_extensions_.khr_portability_subset) {
|
||||
XELOGVK(" * Constant alpha color blend factors: {}",
|
||||
device_portability_subset_features_.constantAlphaColorBlendFactors
|
||||
? "yes"
|
||||
: "no");
|
||||
XELOGVK(" * Image view format reinterpretation: {}",
|
||||
device_portability_subset_features_.imageViewFormatReinterpretation
|
||||
? "yes"
|
||||
: "no");
|
||||
XELOGVK(" * Image view format swizzle: {}",
|
||||
device_portability_subset_features_.imageViewFormatSwizzle ? "yes"
|
||||
: "no");
|
||||
XELOGVK(" * Point polygons: {}",
|
||||
device_portability_subset_features_.pointPolygons ? "yes" : "no");
|
||||
XELOGVK(
|
||||
" * Separate stencil front and back masks and reference values: {}",
|
||||
device_portability_subset_features_.separateStencilMaskRef ? "yes"
|
||||
: "no");
|
||||
XELOGVK(" * Shader sample rate interpolation functions: {}",
|
||||
device_portability_subset_features_
|
||||
.shaderSampleRateInterpolationFunctions
|
||||
? "yes"
|
||||
: "no");
|
||||
XELOGVK(" * Triangle fans: {}",
|
||||
device_portability_subset_features_.triangleFans ? "yes" : "no");
|
||||
}
|
||||
XELOGVK("* VK_KHR_shader_float_controls: {}",
|
||||
device_extensions_.khr_shader_float_controls ? "yes" : "no");
|
||||
if (device_extensions_.khr_shader_float_controls) {
|
||||
|
||||
@@ -39,10 +39,13 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef VK_ENABLE_BETA_EXTENSIONS
|
||||
#define VK_ENABLE_BETA_EXTENSIONS 1
|
||||
#endif
|
||||
#ifndef VK_NO_PROTOTYPES
|
||||
#define VK_NO_PROTOTYPES 1
|
||||
#endif
|
||||
#include "third_party/vulkan/vulkan.h"
|
||||
#include "third_party/Vulkan-Headers/include/vulkan/vulkan.h"
|
||||
|
||||
#define XELOGVK XELOGI
|
||||
|
||||
@@ -133,6 +136,8 @@ class VulkanProvider : public GraphicsProvider {
|
||||
bool khr_dedicated_allocation;
|
||||
// Core since 1.2.0.
|
||||
bool khr_image_format_list;
|
||||
// Requires the VK_KHR_get_physical_device_properties2 instance extension.
|
||||
bool khr_portability_subset;
|
||||
// Core since 1.2.0.
|
||||
bool khr_shader_float_controls;
|
||||
// Core since 1.2.0.
|
||||
@@ -142,6 +147,14 @@ class VulkanProvider : public GraphicsProvider {
|
||||
const DeviceExtensions& device_extensions() const {
|
||||
return device_extensions_;
|
||||
}
|
||||
// Returns nullptr if the device is fully compliant with Vulkan 1.0.
|
||||
const VkPhysicalDevicePortabilitySubsetFeaturesKHR*
|
||||
device_portability_subset_features() const {
|
||||
if (!device_extensions_.khr_portability_subset) {
|
||||
return nullptr;
|
||||
}
|
||||
return &device_portability_subset_features_;
|
||||
}
|
||||
uint32_t memory_types_device_local() const {
|
||||
return memory_types_device_local_;
|
||||
}
|
||||
@@ -269,6 +282,8 @@ class VulkanProvider : public GraphicsProvider {
|
||||
VkPhysicalDeviceProperties device_properties_;
|
||||
VkPhysicalDeviceFeatures device_features_;
|
||||
DeviceExtensions device_extensions_;
|
||||
VkPhysicalDevicePortabilitySubsetFeaturesKHR
|
||||
device_portability_subset_features_;
|
||||
uint32_t memory_types_device_local_;
|
||||
uint32_t memory_types_host_visible_;
|
||||
uint32_t memory_types_host_coherent_;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "xenia/ui/window_win.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@@ -238,27 +239,32 @@ bool Win32Window::OpenImpl() {
|
||||
shown_placement.length = sizeof(shown_placement);
|
||||
if (GetWindowPlacement(hwnd_, &shown_placement)) {
|
||||
// Get the size of the non-client area to subtract it from the size of the
|
||||
// entire window in its non-maximized state, to get the client area.
|
||||
// entire window in its non-maximized state, to get the client area. For
|
||||
// safety, in case the window is somehow smaller than its non-client area
|
||||
// (AdjustWindowRect is not exact in various cases also, such as when the
|
||||
// menu becomes multiline), clamp to 0.
|
||||
RECT non_client_area_rect = {};
|
||||
AdjustWindowRectangle(non_client_area_rect);
|
||||
OnDesiredLogicalSizeUpdate(
|
||||
SizeToLogical(uint32_t(
|
||||
SizeToLogical(uint32_t(std::max(
|
||||
(shown_placement.rcNormalPosition.right -
|
||||
shown_placement.rcNormalPosition.left) -
|
||||
(non_client_area_rect.right - non_client_area_rect.left))),
|
||||
SizeToLogical(uint32_t(
|
||||
(non_client_area_rect.right - non_client_area_rect.left),
|
||||
LONG(0)))),
|
||||
SizeToLogical(uint32_t(std::max(
|
||||
(shown_placement.rcNormalPosition.bottom -
|
||||
shown_placement.rcNormalPosition.top) -
|
||||
(non_client_area_rect.bottom - non_client_area_rect.top))));
|
||||
(non_client_area_rect.bottom - non_client_area_rect.top),
|
||||
LONG(0)))));
|
||||
}
|
||||
|
||||
// Report the actual physical size in the current state.
|
||||
// GetClientRect returns a rectangle with 0 origin.
|
||||
RECT shown_client_rect;
|
||||
if (GetClientRect(hwnd_, &shown_client_rect)) {
|
||||
OnActualSizeUpdate(
|
||||
uint32_t(shown_client_rect.right - shown_client_rect.left),
|
||||
uint32_t(shown_client_rect.bottom - shown_client_rect.top),
|
||||
destruction_receiver);
|
||||
OnActualSizeUpdate(uint32_t(shown_client_rect.right),
|
||||
uint32_t(shown_client_rect.bottom),
|
||||
destruction_receiver);
|
||||
if (destruction_receiver.IsWindowDestroyedOrClosed()) {
|
||||
return true;
|
||||
}
|
||||
@@ -632,17 +638,22 @@ void Win32Window::ApplyFullscreenEntry(
|
||||
// Preserve values for DPI rescaling of the window in the non-maximized state
|
||||
// if DPI is changed mid-fullscreen.
|
||||
// Get the size of the non-client area to subtract it from the size of the
|
||||
// entire window in its non-maximized state, to get the client area.
|
||||
// entire window in its non-maximized state, to get the client area. For
|
||||
// safety, in case the window is somehow smaller than its non-client area
|
||||
// (AdjustWindowRect is not exact in various cases also, such as when the menu
|
||||
// becomes multiline), clamp to 0.
|
||||
RECT non_client_area_rect = {};
|
||||
AdjustWindowRectangle(non_client_area_rect);
|
||||
pre_fullscreen_normal_client_width_ =
|
||||
uint32_t((pre_fullscreen_placement_.rcNormalPosition.right -
|
||||
pre_fullscreen_normal_client_width_ = uint32_t(
|
||||
std::max((pre_fullscreen_placement_.rcNormalPosition.right -
|
||||
pre_fullscreen_placement_.rcNormalPosition.left) -
|
||||
(non_client_area_rect.right - non_client_area_rect.left));
|
||||
pre_fullscreen_normal_client_height_ =
|
||||
uint32_t((pre_fullscreen_placement_.rcNormalPosition.bottom -
|
||||
(non_client_area_rect.right - non_client_area_rect.left),
|
||||
LONG(0)));
|
||||
pre_fullscreen_normal_client_height_ = uint32_t(
|
||||
std::max((pre_fullscreen_placement_.rcNormalPosition.bottom -
|
||||
pre_fullscreen_placement_.rcNormalPosition.top) -
|
||||
(non_client_area_rect.bottom - non_client_area_rect.top));
|
||||
(non_client_area_rect.bottom - non_client_area_rect.top),
|
||||
LONG(0)));
|
||||
|
||||
// Changing the style and the menu may change the size too, don't handle the
|
||||
// resize multiple times (also potentially with the listeners changing the
|
||||
@@ -716,27 +727,32 @@ void Win32Window::HandleSizeUpdate(
|
||||
// window_placement.rcNormalPosition is the entire window's rectangle, not
|
||||
// only the client area - convert to client.
|
||||
// https://devblogs.microsoft.com/oldnewthing/20131017-00/?p=2903
|
||||
RECT non_client_rect = {};
|
||||
if (AdjustWindowRectangle(non_client_rect)) {
|
||||
// For safety, in case the window is somehow smaller than its non-client
|
||||
// area (AdjustWindowRect is not exact in various cases also, such as when
|
||||
// the menu becomes multiline), clamp to 0.
|
||||
RECT non_client_area_rect = {};
|
||||
if (AdjustWindowRectangle(non_client_area_rect)) {
|
||||
OnDesiredLogicalSizeUpdate(
|
||||
SizeToLogical(uint32_t((window_placement.rcNormalPosition.right -
|
||||
non_client_rect.right) -
|
||||
(window_placement.rcNormalPosition.left -
|
||||
non_client_rect.left))),
|
||||
SizeToLogical(uint32_t((window_placement.rcNormalPosition.bottom -
|
||||
non_client_rect.bottom) -
|
||||
(window_placement.rcNormalPosition.top -
|
||||
non_client_rect.top))));
|
||||
SizeToLogical(uint32_t(std::max(
|
||||
(window_placement.rcNormalPosition.right -
|
||||
window_placement.rcNormalPosition.left) -
|
||||
(non_client_area_rect.right - non_client_area_rect.left),
|
||||
LONG(0)))),
|
||||
SizeToLogical(uint32_t(std::max(
|
||||
(window_placement.rcNormalPosition.bottom -
|
||||
window_placement.rcNormalPosition.top) -
|
||||
(non_client_area_rect.bottom - non_client_area_rect.top),
|
||||
LONG(0)))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For the actual state.
|
||||
// GetClientRect returns a rectangle with 0 origin.
|
||||
RECT client_rect;
|
||||
if (GetClientRect(hwnd_, &client_rect)) {
|
||||
OnActualSizeUpdate(uint32_t(client_rect.right - client_rect.left),
|
||||
uint32_t(client_rect.bottom - client_rect.top),
|
||||
destruction_receiver);
|
||||
OnActualSizeUpdate(uint32_t(client_rect.right),
|
||||
uint32_t(client_rect.bottom), destruction_receiver);
|
||||
if (destruction_receiver.IsWindowDestroyedOrClosed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ Win32WindowedAppContext::~Win32WindowedAppContext() {
|
||||
if (pending_functions_hwnd_) {
|
||||
DestroyWindow(pending_functions_hwnd_);
|
||||
}
|
||||
if (user32_module_) {
|
||||
FreeLibrary(user32_module_);
|
||||
}
|
||||
if (shcore_module_) {
|
||||
FreeLibrary(shcore_module_);
|
||||
}
|
||||
@@ -38,25 +41,28 @@ bool Win32WindowedAppContext::Initialize() {
|
||||
(*reinterpret_cast<void**>(
|
||||
&per_monitor_dpi_v1_api_.get_dpi_for_monitor) =
|
||||
GetProcAddress(shcore_module_, "GetDpiForMonitor")) != nullptr;
|
||||
}
|
||||
user32_module_ = LoadLibraryW(L"user32.dll");
|
||||
if (user32_module_) {
|
||||
per_monitor_dpi_v2_api_available_ = true;
|
||||
per_monitor_dpi_v2_api_available_ &=
|
||||
(*reinterpret_cast<void**>(
|
||||
&per_monitor_dpi_v2_api_.adjust_window_rect_ex_for_dpi) =
|
||||
GetProcAddress(shcore_module_, "AdjustWindowRectExForDpi")) !=
|
||||
GetProcAddress(user32_module_, "AdjustWindowRectExForDpi")) !=
|
||||
nullptr;
|
||||
per_monitor_dpi_v2_api_available_ &=
|
||||
(*reinterpret_cast<void**>(
|
||||
&per_monitor_dpi_v2_api_.enable_non_client_dpi_scaling) =
|
||||
GetProcAddress(shcore_module_, "EnableNonClientDpiScaling")) !=
|
||||
GetProcAddress(user32_module_, "EnableNonClientDpiScaling")) !=
|
||||
nullptr;
|
||||
per_monitor_dpi_v2_api_available_ &=
|
||||
(*reinterpret_cast<void**>(
|
||||
&per_monitor_dpi_v2_api_.get_dpi_for_system) =
|
||||
GetProcAddress(shcore_module_, "GetDpiForSystem")) != nullptr;
|
||||
GetProcAddress(user32_module_, "GetDpiForSystem")) != nullptr;
|
||||
per_monitor_dpi_v2_api_available_ &=
|
||||
(*reinterpret_cast<void**>(
|
||||
&per_monitor_dpi_v2_api_.get_dpi_for_window) =
|
||||
GetProcAddress(shcore_module_, "GetDpiForWindow")) != nullptr;
|
||||
GetProcAddress(user32_module_, "GetDpiForWindow")) != nullptr;
|
||||
}
|
||||
|
||||
// Create the message-only window for executing pending functions - using a
|
||||
|
||||
@@ -97,6 +97,7 @@ class Win32WindowedAppContext final : public WindowedAppContext {
|
||||
int show_cmd_;
|
||||
|
||||
HMODULE shcore_module_ = nullptr;
|
||||
HMODULE user32_module_ = nullptr;
|
||||
PerMonitorDpiV1Api per_monitor_dpi_v1_api_ = {};
|
||||
PerMonitorDpiV2Api per_monitor_dpi_v2_api_ = {};
|
||||
bool per_monitor_dpi_v1_api_available_ = false;
|
||||
|
||||
Reference in New Issue
Block a user