diff --git a/src/xenia/gpu/gl4/gl4_command_processor.cc b/src/xenia/gpu/gl4/gl4_command_processor.cc index 9855b137f..de191e371 100644 --- a/src/xenia/gpu/gl4/gl4_command_processor.cc +++ b/src/xenia/gpu/gl4/gl4_command_processor.cc @@ -396,19 +396,24 @@ void GL4CommandProcessor::PerformSwap(uint32_t frontbuffer_ptr, // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // HACK: just use whatever our current framebuffer is. GLuint framebuffer_texture = last_framebuffer_texture_; - /*GLuint framebuffer_texture = active_framebuffer_ - ? active_framebuffer_->color_targets[0] - : last_framebuffer_texture_;*/ + + if (last_framebuffer_texture_ == 0) { + framebuffer_texture = + active_framebuffer_ ? active_framebuffer_->color_targets[0] : 0; + } // Copy the the given framebuffer to the current backbuffer. Rect2D src_rect(0, 0, frontbuffer_width ? frontbuffer_width : 1280, frontbuffer_height ? frontbuffer_height : 720); Rect2D dest_rect(0, 0, swap_state_.width, swap_state_.height); - reinterpret_cast(context_.get()) - ->blitter() - ->CopyColorTexture2D(framebuffer_texture, src_rect, - static_cast(swap_state_.back_buffer_texture), - dest_rect, GL_LINEAR, true); + if (framebuffer_texture != 0) { + reinterpret_cast(context_.get()) + ->blitter() + ->CopyColorTexture2D( + framebuffer_texture, src_rect, + static_cast(swap_state_.back_buffer_texture), dest_rect, + GL_LINEAR, true); + } if (FLAGS_draw_all_framebuffers) { int32_t offsetx = (1280 - (1280 / 5)); @@ -465,6 +470,12 @@ void GL4CommandProcessor::PerformSwap(uint32_t frontbuffer_ptr, // TODO(benvanik): prevent this? fences? glFinish(); + if (context_->WasLost()) { + // We've lost the context due to a TDR. + // TODO: Dump the current commands to a tracefile. + assert_always(); + } + // Remove any dead textures, etc. texture_cache_.Scavenge(); } @@ -581,8 +592,15 @@ bool GL4CommandProcessor::IssueDraw(PrimitiveType prim_type, if (!draw_batcher_.CommitDraw()) { return false; } + // TODO(benvanik): find a way to get around glVertexArrayVertexBuffer below. draw_batcher_.Flush(DrawBatcher::FlushMode::kMakeCoherent); + if (context_->WasLost()) { + // This draw lost us the context. This typically isn't hit. + assert_always(); + return false; + } + return true; } diff --git a/src/xenia/gpu/gl4/gl4_shader.cc b/src/xenia/gpu/gl4/gl4_shader.cc index 19a508345..d461cbfd2 100644 --- a/src/xenia/gpu/gl4/gl4_shader.cc +++ b/src/xenia/gpu/gl4/gl4_shader.cc @@ -135,18 +135,24 @@ bool GL4Shader::CompileProgram() { return false; } + // Output info log. + // log_length includes the null character. + GLint log_length = 0; + glGetProgramiv(program_, GL_INFO_LOG_LENGTH, &log_length); + std::string info_log; + if (log_length > 0) { + info_log.resize(log_length - 1); + glGetProgramInfoLog(program_, log_length, &log_length, &info_log[0]); + } + + if (!info_log.empty()) { + XELOGD("Shader log: %s", info_log.c_str()); + } + // Get error log, if we failed to link. GLint link_status = 0; glGetProgramiv(program_, GL_LINK_STATUS, &link_status); if (!link_status) { - // log_length includes the null character. - GLint log_length = 0; - glGetProgramiv(program_, GL_INFO_LOG_LENGTH, &log_length); - std::string info_log; - info_log.resize(log_length - 1); - glGetProgramInfoLog(program_, log_length, &log_length, - const_cast(info_log.data())); - XELOGE("Unable to link program: %s", info_log.c_str()); host_error_log_ = std::move(info_log); assert_always("Unable to link generated shader"); return false; diff --git a/src/xenia/gpu/glsl_shader_translator.cc b/src/xenia/gpu/glsl_shader_translator.cc index 9311f5a1c..423ba191d 100644 --- a/src/xenia/gpu/glsl_shader_translator.cc +++ b/src/xenia/gpu/glsl_shader_translator.cc @@ -361,10 +361,10 @@ void GlslShaderTranslator::ProcessExecInstructionBegin( EmitSourceDepth("{\n"); break; case ParsedExecInstruction::Type::kConditional: - EmitSourceDepth("if ((state.bool_consts[%d] & (1 << %d)) == %c) {\n", + EmitSourceDepth("if ((state.bool_consts[%d] & (1 << %d)) %c= 0) {\n", instr.bool_constant_index / 32, instr.bool_constant_index % 32, - instr.condition ? '1' : '0'); + instr.condition ? '!' : '='); break; case ParsedExecInstruction::Type::kPredicated: EmitSourceDepth("if (%cp0) {\n", instr.condition ? ' ' : '!'); @@ -384,13 +384,13 @@ void GlslShaderTranslator::ProcessExecInstructionBegin( void GlslShaderTranslator::ProcessExecInstructionEnd( const ParsedExecInstruction& instr) { - Unindent(); - EmitSourceDepth("}\n"); if (instr.is_end) { EmitSourceDepth("pc = 0xFFFF;\n"); EmitSourceDepth("break;\n"); cf_wrote_pc_ = true; } + Unindent(); + EmitSourceDepth("}\n"); } void GlslShaderTranslator::ProcessLoopStartInstruction( @@ -494,10 +494,10 @@ void GlslShaderTranslator::ProcessJumpInstruction( EmitSourceDepth("{\n"); break; case ParsedJumpInstruction::Type::kConditional: - EmitSourceDepth("if ((state.bool_consts[%d] & (1 << %d)) == %c) {\n", + EmitSourceDepth("if ((state.bool_consts[%d] & (1 << %d)) %c= 0) {\n", instr.bool_constant_index / 32, instr.bool_constant_index % 32, - instr.condition ? '1' : '0'); + instr.condition ? '!' : '='); needs_fallthrough = true; break; case ParsedJumpInstruction::Type::kPredicated: diff --git a/src/xenia/gpu/trace_player.cc b/src/xenia/gpu/trace_player.cc index aea69e119..785db006d 100644 --- a/src/xenia/gpu/trace_player.cc +++ b/src/xenia/gpu/trace_player.cc @@ -156,6 +156,7 @@ void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data, pending_packet = nullptr; } if (pending_break) { + playing_trace_ = false; return; } break; diff --git a/src/xenia/ui/gl/blitter.cc b/src/xenia/ui/gl/blitter.cc index 21c2aec36..7501758ca 100644 --- a/src/xenia/ui/gl/blitter.cc +++ b/src/xenia/ui/gl/blitter.cc @@ -202,6 +202,8 @@ struct SavedState { void Blitter::Draw(GLuint src_texture, Rect2D src_rect, Rect2D dest_rect, GLenum filter) { + assert_not_zero(src_texture); + glDisable(GL_SCISSOR_TEST); glDisable(GL_STENCIL_TEST); glDisablei(GL_BLEND, 0); diff --git a/src/xenia/ui/gl/gl_context.cc b/src/xenia/ui/gl/gl_context.cc index 7a6793bbf..7a3da67c1 100644 --- a/src/xenia/ui/gl/gl_context.cc +++ b/src/xenia/ui/gl/gl_context.cc @@ -133,20 +133,30 @@ bool GLContext::Initialize(GLContext* share_context) { return false; } + if (GLEW_ARB_robustness) { + robust_access_supported_ = true; + } + int context_flags = 0; if (FLAGS_gl_debug) { context_flags |= WGL_CONTEXT_DEBUG_BIT_ARB; } + if (robust_access_supported_) { + context_flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB; + } - int attrib_list[] = {WGL_CONTEXT_MAJOR_VERSION_ARB, - 4, - WGL_CONTEXT_MINOR_VERSION_ARB, - 5, - WGL_CONTEXT_FLAGS_ARB, - context_flags, - WGL_CONTEXT_PROFILE_MASK_ARB, - WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, - 0}; + int attrib_list[] = { + WGL_CONTEXT_MAJOR_VERSION_ARB, + 4, + WGL_CONTEXT_MINOR_VERSION_ARB, + 5, + WGL_CONTEXT_FLAGS_ARB, + context_flags, + WGL_CONTEXT_PROFILE_MASK_ARB, + WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + robust_access_supported_ ? WGL_LOSE_CONTEXT_ON_RESET_ARB : 0, + 0}; glrc_ = wglCreateContextAttribsARB( dc_, share_context ? share_context->glrc_ : nullptr, attrib_list); @@ -201,15 +211,23 @@ std::unique_ptr GLContext::CreateOffscreen( context_flags |= WGL_CONTEXT_DEBUG_BIT_ARB; } - int attrib_list[] = {WGL_CONTEXT_MAJOR_VERSION_ARB, - 4, - WGL_CONTEXT_MINOR_VERSION_ARB, - 5, - WGL_CONTEXT_FLAGS_ARB, - context_flags, - WGL_CONTEXT_PROFILE_MASK_ARB, - WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, - 0}; + bool robust_access_supported = parent_context->robust_access_supported_; + if (robust_access_supported) { + context_flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB; + } + + int attrib_list[] = { + WGL_CONTEXT_MAJOR_VERSION_ARB, + 4, + WGL_CONTEXT_MINOR_VERSION_ARB, + 5, + WGL_CONTEXT_FLAGS_ARB, + context_flags, + WGL_CONTEXT_PROFILE_MASK_ARB, + WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + robust_access_supported ? WGL_LOSE_CONTEXT_ON_RESET_ARB : 0, + 0}; new_glrc = wglCreateContextAttribsARB(parent_context->dc_, parent_context->glrc_, attrib_list); if (!new_glrc) { @@ -223,6 +241,8 @@ std::unique_ptr GLContext::CreateOffscreen( new_context->glrc_ = new_glrc; new_context->dc_ = GetDC(HWND(parent_context->target_window_->native_handle())); + new_context->robust_access_supported_ = + parent_context->robust_access_supported_; if (!new_context->MakeCurrent()) { FatalGLError("Could not make new GL context current."); return nullptr; @@ -281,6 +301,8 @@ void GLContext::AssertExtensionsPresent() { "OpenGL extension ARB_fragment_coord_conventions is required."); return; } + + ClearCurrent(); } void GLContext::DebugMessage(GLenum source, GLenum type, GLuint id, @@ -448,6 +470,28 @@ void GLContext::ClearCurrent() { } } +bool GLContext::WasLost() { + if (!robust_access_supported_) { + // Can't determine if we lost the context. + return false; + } + + if (context_lost_) { + return true; + } + + auto status = glGetGraphicsResetStatusARB(); + if (status != GL_NO_ERROR) { + // Graphics card reset. + XELOGE("============= TDR detected on context %p! Context %s =============", + glrc_, status == GL_GUILTY_CONTEXT_RESET ? "guilty" : "innocent"); + context_lost_ = true; + return true; + } + + return false; +} + void GLContext::BeginSwap() { SCOPE_profile_cpu_i("gpu", "xe::ui::gl::GLContext::BeginSwap"); float clear_color[] = {238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 1.0f}; diff --git a/src/xenia/ui/gl/gl_context.h b/src/xenia/ui/gl/gl_context.h index 65d2beb5a..a60e2d4ed 100644 --- a/src/xenia/ui/gl/gl_context.h +++ b/src/xenia/ui/gl/gl_context.h @@ -40,6 +40,7 @@ class GLContext : public GraphicsContext { bool is_current() override; bool MakeCurrent() override; void ClearCurrent() override; + bool WasLost() override; void BeginSwap() override; void EndSwap() override; @@ -80,6 +81,9 @@ class GLContext : public GraphicsContext { Blitter blitter_; std::unique_ptr immediate_drawer_; + + bool context_lost_ = false; + bool robust_access_supported_ = false; }; } // namespace gl diff --git a/src/xenia/ui/graphics_context.h b/src/xenia/ui/graphics_context.h index 1ca00dbc8..ef352099f 100644 --- a/src/xenia/ui/graphics_context.h +++ b/src/xenia/ui/graphics_context.h @@ -45,6 +45,12 @@ class GraphicsContext { virtual bool MakeCurrent() = 0; virtual void ClearCurrent() = 0; + // Returns true if the OS took away our context because we caused a TDR or + // some other outstanding error. When this happens, this context, as well as + // any other shared contexts are junk. + // This context must be made current in order for this call to work properly. + virtual bool WasLost() { return false; } + virtual void BeginSwap() = 0; virtual void EndSwap() = 0;