@ -2,58 +2,203 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
# include <cstring>
# include <memory>
# include <utility>
# include <vector>
# include <glad/glad.h>
# include "common/assert.h"
# include "core/core.h"
# include "video_core/engines/maxwell_3d.h"
# include "video_core/memory_manager.h"
# include "video_core/renderer_opengl/gl_query_cache.h"
# include "video_core/renderer_opengl/gl_rasterizer.h"
namespace OpenGL {
HostCounter : : HostCounter ( GLenum target ) {
query . Create ( target ) ;
using VideoCore : : QueryType ;
namespace {
constexpr std : : array < GLenum , VideoCore : : NumQueryTypes > QueryTargets = { GL_SAMPLES_PASSED } ;
constexpr GLenum GetTarget ( QueryType type ) {
return QueryTargets [ static_cast < std : : size_t > ( type ) ] ;
}
HostCounter : : ~ HostCounter ( ) = default ;
} // Anonymous namespace
void HostCounter : : UpdateState ( bool enabled ) {
CounterStream : : CounterStream ( QueryCache & cache , QueryType type )
: cache { cache } , type { type } , target { GetTarget ( type ) } { }
CounterStream : : ~ CounterStream ( ) = default ;
void CounterStream : : Update ( bool enabled , bool any_command_queued ) {
if ( enabled ) {
Enable ( ) ;
} else {
Disable ( ) ;
if ( ! current ) {
current = cache . GetHostCounter ( last , type ) ;
}
return ;
}
if ( current ) {
EndQuery ( any_command_queued ) ;
}
last = std : : exchange ( current , nullptr ) ;
}
void HostCounter : : Reset ( ) {
counter = 0 ;
Disable ( ) ;
void CounterStream : : Reset ( bool any_command_queued ) {
if ( current ) {
EndQuery ( any_command_queued ) ;
}
current = nullptr ;
last = nullptr ;
}
u64 HostCounter : : Query ( ) {
if ( ! is_beginned ) {
return counte r;
std: : shared_ptr < HostCounter > CounterStream : : GetCurrent ( bool any_command_queued ) {
if ( ! current ) {
return nullpt r;
}
Disable ( ) ;
u64 value ;
glGetQueryObjectui64v ( query . handle , GL_QUERY_RESULT , & value ) ;
Enable ( ) ;
EndQuery ( any_command_queued ) ;
last = std : : move ( current ) ;
current = cache . GetHostCounter ( last , type ) ;
return last ;
}
counter + = value ;
void CounterStream : : EndQuery ( bool any_command_queued ) {
if ( ! any_command_queued ) {
// There are chances a query waited on without commands (glDraw, glClear, glDispatch). Not
// having any of these causes a lock. glFlush is considered a command, so we can safely wait
// for this. Insert to the OpenGL command stream a flush.
glFlush ( ) ;
}
glEndQuery ( target ) ;
}
QueryCache : : QueryCache ( Core : : System & system , RasterizerOpenGL & rasterizer )
: RasterizerCache { rasterizer } , system { system } ,
rasterizer { rasterizer } , streams { { CounterStream { * this , QueryType : : SamplesPassed } } } { }
QueryCache : : ~ QueryCache ( ) = default ;
void QueryCache : : Query ( GPUVAddr gpu_addr , QueryType type ) {
auto & memory_manager = system . GPU ( ) . MemoryManager ( ) ;
const auto host_ptr = memory_manager . GetPointer ( gpu_addr ) ;
auto query = TryGet ( host_ptr ) ;
if ( ! query ) {
const auto cpu_addr = memory_manager . GpuToCpuAddress ( gpu_addr ) ;
ASSERT_OR_EXECUTE ( cpu_addr , return ; ) ;
query = std : : make_shared < CachedQuery > ( type , * cpu_addr , host_ptr ) ;
Register ( query ) ;
}
query - > SetCounter ( GetStream ( type ) . GetCurrent ( rasterizer . AnyCommandQueued ( ) ) ) ;
query - > MarkAsModified ( true , * this ) ;
}
void QueryCache : : UpdateCounters ( ) {
auto & samples_passed = GetStream ( QueryType : : SamplesPassed ) ;
const auto & regs = system . GPU ( ) . Maxwell3D ( ) . regs ;
samples_passed . Update ( regs . samplecnt_enable , rasterizer . AnyCommandQueued ( ) ) ;
}
void QueryCache : : ResetCounter ( QueryType type ) {
GetStream ( type ) . Reset ( rasterizer . AnyCommandQueued ( ) ) ;
}
void QueryCache : : Reserve ( QueryType type , OGLQuery & & query ) {
reserved_queries [ static_cast < std : : size_t > ( type ) ] . push_back ( std : : move ( query ) ) ;
}
std : : shared_ptr < HostCounter > QueryCache : : GetHostCounter ( std : : shared_ptr < HostCounter > dependency ,
QueryType type ) {
const auto type_index = static_cast < std : : size_t > ( type ) ;
auto & reserve = reserved_queries [ type_index ] ;
if ( reserve . empty ( ) ) {
return std : : make_shared < HostCounter > ( * this , std : : move ( dependency ) , type ) ;
}
auto counter = std : : make_shared < HostCounter > ( * this , std : : move ( dependency ) , type ,
std : : move ( reserve . back ( ) ) ) ;
reserve . pop_back ( ) ;
return counter ;
}
void HostCounter : : Enable ( ) {
if ( is_beginned ) {
return ;
void QueryCache : : FlushObjectInner ( const std : : shared_ptr < CachedQuery > & counter_ ) {
auto & counter = * counter_ ;
auto & stream = GetStream ( counter . GetType ( ) ) ;
// Waiting for a query while another query of the same target is enabled locks Nvidia's driver.
// To avoid this disable and re-enable keeping the dependency stream.
const bool is_enabled = stream . IsEnabled ( ) ;
if ( is_enabled ) {
stream . Update ( false , false ) ;
}
counter . Flush ( ) ;
if ( is_enabled ) {
stream . Update ( true , false ) ;
}
is_beginned = true ;
glBeginQuery ( GL_SAMPLES_PASSED , query . handle ) ;
}
void HostCounter : : Disable ( ) {
if ( ! is_beginned ) {
return ;
CounterStream & QueryCache : : GetStream ( QueryType type ) {
return streams [ static_cast < std : : size_t > ( type ) ] ;
}
HostCounter : : HostCounter ( QueryCache & cache , std : : shared_ptr < HostCounter > dependency , QueryType type )
: cache { cache } , type { type } , dependency { std : : move ( dependency ) } {
const GLenum target = GetTarget ( type ) ;
query . Create ( target ) ;
glBeginQuery ( target , query . handle ) ;
}
HostCounter : : HostCounter ( QueryCache & cache , std : : shared_ptr < HostCounter > dependency , QueryType type ,
OGLQuery & & query_ )
: cache { cache } , type { type } , dependency { std : : move ( dependency ) } , query { std : : move ( query_ ) } {
glBeginQuery ( GetTarget ( type ) , query . handle ) ;
}
HostCounter : : ~ HostCounter ( ) {
cache . Reserve ( type , std : : move ( query ) ) ;
}
u64 HostCounter : : Query ( ) {
if ( query . handle = = 0 ) {
return result ;
}
glEndQuery ( GL_SAMPLES_PASSED ) ;
is_beginned = false ;
glGetQueryObjectui64v ( query . handle , GL_QUERY_RESULT , & result ) ;
if ( dependency ) {
result + = dependency - > Query ( ) ;
}
return result ;
}
CachedQuery : : CachedQuery ( QueryType type , VAddr cpu_addr , u8 * host_ptr )
: RasterizerCacheObject { host_ptr } , type { type } , cpu_addr { cpu_addr } , host_ptr { host_ptr } { }
CachedQuery : : ~ CachedQuery ( ) = default ;
void CachedQuery : : Flush ( ) {
const u64 value = counter - > Query ( ) ;
std : : memcpy ( host_ptr , & value , sizeof ( value ) ) ;
}
void CachedQuery : : SetCounter ( std : : shared_ptr < HostCounter > counter_ ) {
counter = std : : move ( counter_ ) ;
}
QueryType CachedQuery : : GetType ( ) const {
return type ;
}
} // namespace OpenGL