[Feat] (Shader): Emulate atomicCounter advanced operations.

This commit is contained in:
BZLZHH 2025-07-27 20:49:39 +08:00
parent cea5d7ab81
commit a2b4aa69f8
7 changed files with 143 additions and 63 deletions

View File

@ -19,6 +19,7 @@ GLuint bufSampelerLoc;
std::string bufSampelerName;
extern std::unordered_map<GLuint, bool> program_map_is_sampler_buffer_emulated;
extern std::unordered_map<GLuint, bool> program_map_is_atomic_counter_emulated;
unordered_map<GLuint, SamplerInfo> g_samplerCacheForSamplerBuffer;
@ -99,10 +100,6 @@ void prepareForDraw() {
}
}
// solve the crash error for ANGLE, but it will make Derivative Main with Optifine not work!
//_Thread_local static bool unexpected_error = false;
void glDrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei primcount) {
LOG()
LOG_D("glDrawElementsInstanced, mode: %d, count: %d, type: %d, indices: %p, primcount: %d",
@ -116,67 +113,73 @@ void glDrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices
LOG()
LOG_D("glDrawElements, mode: %d, count: %d, type: %d, indices: %p", mode, count, type, indices)
prepareForDraw();
//LOAD_GLES_FUNC(glGetError)
//GLenum pre_err = GLES.glGetError();
//if(pre_err != GL_NO_ERROR) {
// LOG_D("Skipping due to prior error: 0x%04X", pre_err)
// return;
//}
//if (!unexpected_error) {
// LOG_D("es_glDrawElements, mode: %d, count: %d, type: %d, indices: %p", mode, count, type, indices)
GLES.glDrawElements(mode, count, type, indices);
CHECK_GL_ERROR
//} else {
// unexpected_error = false;
//}
}
void glBindImageTexture(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) {
LOG()
LOG_D("glBindImageTexture, unit: %d, texture: %d, level: %d, layered: %d, layer: %d, access: %d, format: %d",
unit, texture, level, layered, layer, access, format)
//LOAD_GLES_FUNC(glGetError)
GLES.glBindImageTexture(unit, texture, level, layered, layer, access, format);
CHECK_GL_ERROR
//GLenum err;
//while((err = GLES.glGetError()) != GL_NO_ERROR) {
// LOG_D("GL Error: 0x%04X", err)
// unexpected_error = true;
//}
}
void glUniform1i(GLint location, GLint v0) {
LOG()
LOG_D("glUniform1i, location: %d, v0: %d", location, v0)
//LOAD_GLES_FUNC(glGetError)
GLES.glUniform1i(location, v0);
CHECK_GL_ERROR
//GLenum err;
//while((err = GLES.glGetError()) != GL_NO_ERROR) {
// LOG_D("GL Error: 0x%04X", err)
// unexpected_error = true;
//}
}
void bindAllAtomicCounterAsSSBO() {
GLint maxBindings = 0;
GLES.glGetIntegerv(GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS, &maxBindings);
if (maxBindings <= 0) {
LOG_W("No atomic counter buffer bindings available, maxBindings: %d", maxBindings);
return;
}
std::vector<GLuint> buffers(maxBindings, 0);
for (GLint i = 0; i < maxBindings; ++i) {
GLES.glGetIntegeri_v(GL_ATOMIC_COUNTER_BUFFER_BINDING, i, reinterpret_cast<GLint*>(&buffers[i]));
}
for (GLint i = 0; i < maxBindings; ++i) {
GLuint buf = buffers[i];
if (buf != 0) {
GLES.glBindBufferBase(GL_SHADER_STORAGE_BUFFER, i, buf);
}
}
LOG_D("Bound %d atomic counter buffers as SSBOs", maxBindings);
}
void glDispatchCompute(GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z) {
LOG()
LOG_D("glDispatchCompute, num_groups_x: %d, num_groups_y: %d, num_groups_z: %d",
num_groups_x, num_groups_y, num_groups_z)
//LOAD_GLES_FUNC(glGetError)
//GLenum pre_err = GLES.glGetError();
//if(pre_err != GL_NO_ERROR) {
// LOG_D("Skipping due to prior error: 0x%04X", pre_err)
// return;
//}
//if (!unexpected_error) {
// LOG_D("es_glDispatchCompute, num_groups_x: %d, num_groups_y: %d, num_groups_z: %d",
// num_groups_x, num_groups_y, num_groups_z)
if (program_map_is_atomic_counter_emulated[gl_state->current_program]) {
bindAllAtomicCounterAsSSBO();
LOG_D("Atomic counters bound as SSBOs for program %d", gl_state->current_program);
}
else {
LOG_D("No atomic counters bound as SSBOs for program %d", gl_state->current_program);
}
GLES.glDispatchCompute(num_groups_x, num_groups_y, num_groups_z);
CHECK_GL_ERROR
//} else {
// unexpected_error = false;
//}
}
void glMemoryBarrier(GLbitfield barriers) {
LOG()
LOG_D("glMemoryBarrier, barriers: %d", barriers)
if (program_map_is_atomic_counter_emulated[gl_state->current_program]) {
barriers |= GL_ATOMIC_COUNTER_BARRIER_BIT;
barriers |= GL_SHADER_STORAGE_BARRIER_BIT;
}
GLES.glMemoryBarrier(barriers);
CHECK_GL_ERROR
}
void glDrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, GLint basevertex) {

View File

@ -35,6 +35,7 @@ GLAPI GLAPIENTRY void glDrawElements(GLenum mode, GLsizei count, GLenum type, co
GLAPI GLAPIENTRY void glBindImageTexture(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format);
GLAPI GLAPIENTRY void glDispatchCompute(GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z);
GLAPI GLAPIENTRY void glMemoryBarrier(GLbitfield barriers);
GLAPI GLAPIENTRY void glUniform1i(GLint location, GLint v0);
#ifdef __cplusplus

View File

@ -311,7 +311,7 @@ NATIVE_FUNCTION_HEAD(void, glValidateProgramPipeline, GLuint pipeline) NATIVE_FU
NATIVE_FUNCTION_HEAD(void, glGetProgramPipelineInfoLog, GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog) NATIVE_FUNCTION_END_NO_RETURN(void, glGetProgramPipelineInfoLog, pipeline,bufSize,length,infoLog)
//NATIVE_FUNCTION_HEAD(void, glBindImageTexture, GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) NATIVE_FUNCTION_END_NO_RETURN(void, glBindImageTexture, unit,texture,level,layered,layer,access,format)
NATIVE_FUNCTION_HEAD(void, glGetBooleani_v, GLenum target, GLuint index, GLboolean *data) NATIVE_FUNCTION_END_NO_RETURN(void, glGetBooleani_v, target,index,data)
NATIVE_FUNCTION_HEAD(void, glMemoryBarrier, GLbitfield barriers) NATIVE_FUNCTION_END_NO_RETURN(void, glMemoryBarrier, barriers)
//NATIVE_FUNCTION_HEAD(void, glMemoryBarrier, GLbitfield barriers) NATIVE_FUNCTION_END_NO_RETURN(void, glMemoryBarrier, barriers)
NATIVE_FUNCTION_HEAD(void, glMemoryBarrierByRegion, GLbitfield barriers) NATIVE_FUNCTION_END_NO_RETURN(void, glMemoryBarrierByRegion, barriers)
NATIVE_FUNCTION_HEAD(void, glTexStorage2DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) NATIVE_FUNCTION_END_NO_RETURN(void, glTexStorage2DMultisample, target,samples,internalformat,width,height,fixedsamplelocations)
NATIVE_FUNCTION_HEAD(void, glGetMultisamplefv, GLenum pname, GLuint index, GLfloat *val) NATIVE_FUNCTION_END_NO_RETURN(void, glGetMultisamplefv, pname,index,val)

View File

@ -16,7 +16,9 @@
#include "cache.h"
#include "../../version.h"
#define DEBUG 0
#define DEBUG 0
const char* atomicCounterEmulatedWatermark = "\n// Non-opaque atomic uniform converted to SSBO\n";
#if !defined(__APPLE__)
char* (*MesaConvertShader)(const char *src, unsigned int type, unsigned int glsl, unsigned int essl);
@ -335,33 +337,29 @@ std::string processOutColorLocations(const std::string& glslCode) {
return std::regex_replace(glslCode, pattern, replacement);
}
std::string getCachedESSL(const char* glsl_code, uint essl_version) {
std::string sha256_string(glsl_code);
sha256_string += "\n//" + std::to_string(MAJOR) + "." + std::to_string(MINOR) + "." + std::to_string(REVISION) + "|" + std::to_string(essl_version);
const char* cachedESSL = Cache::get_instance().get(sha256_string.c_str());
if (cachedESSL) {
LOG_D("GLSL Hit Cache:\n%s\n-->\n%s", glsl_code, cachedESSL)
return cachedESSL;
} else return "";
bool checkIfAtomicCounterBufferEmulated(const std::string& glslCode) {
return glslCode.find(atomicCounterEmulatedWatermark) != std::string::npos;
}
std::string GLSLtoGLSLES(const char* glsl_code, GLenum glsl_type, uint essl_version, uint glsl_version) {
std::string GLSLtoGLSLES(const char* glsl_code, GLenum glsl_type, uint essl_version, uint glsl_version, int& return_code) {
std::string sha256_string(glsl_code);
sha256_string += "\n//" + std::to_string(MAJOR) + "." + std::to_string(MINOR) + "." + std::to_string(REVISION) + "|" + std::to_string(essl_version);
const char* cachedESSL = Cache::get_instance().get(sha256_string.c_str());
if (cachedESSL) {
LOG_D("GLSL Hit Cache:\n%s\n-->\n%s", glsl_code, cachedESSL)
bool atomicCounterEmulated = checkIfAtomicCounterBufferEmulated(std::string(cachedESSL));
return_code = atomicCounterEmulated ? 1 : 0;
return (char*)cachedESSL;
}
int return_code = -1;
return_code = -1;
std::string converted = glsl_version<140? GLSLtoGLSLES_1(glsl_code, glsl_type, essl_version, return_code):GLSLtoGLSLES_2(glsl_code, glsl_type, essl_version, return_code);
if (return_code == 0 && !converted.empty()) {
if (return_code >= 0 && !converted.empty()) {
converted = process_uniform_declarations(converted);
Cache::get_instance().put(sha256_string.c_str(), converted.c_str());
}
return (return_code == 0) ? converted : glsl_code;
return (return_code >= 0) ? converted : glsl_code;
}
std::string replace_line_starting_with(const std::string& glslCode, const std::string& starting, const std::string& substitution = "") {
@ -487,6 +485,70 @@ static size_t find_insertion_point(const std::string& glsl) {
return insertion_point;
}
bool process_non_opaque_atomic_to_ssbo(std::string& source) {
if (source.find("atomicCounter") == std::string::npos) return false;
std::set<std::string> atomic_vars;
std::map<std::string, std::string> binding_map;
std::regex decl_rx(
R"(layout\s*\(\s*binding\s*=\s*(\d+)\s*(?:,\s*offset\s*=\s*(\d+)\s*)?\)\s*uniform\s+atomic_uint\s+(\w+)\s*;)",
std::regex::icase
);
std::smatch m;
auto it = source.cbegin();
while (std::regex_search(it, source.cend(), m, decl_rx)) {
size_t prefix = std::distance(source.cbegin(), it);
size_t match_pos = prefix + m.position(0);
size_t match_len = m.length(0);
std::string binding = m[1].str();
std::string var = m[3].str();
atomic_vars.insert(var);
binding_map[var] = binding;
std::string repl =
"layout(std430, binding=" + binding + ") buffer AtomicCounterSSBO_" + binding + " {\n"
" uint " + var + ";\n"
"};\n";
source.replace(match_pos, match_len, repl);
it = source.cbegin() + match_pos + repl.size();
}
if (atomic_vars.empty()) return true;
for (auto& var : atomic_vars) {
source = std::regex_replace(source,
std::regex(R"(\batomicCounterIncrement\s*\(\s*)" + var + R"(\s*\))", std::regex::icase),
"atomicAdd(" + var + ", 1u)"
);
source = std::regex_replace(source,
std::regex(R"(\batomicCounterDecrement\s*\(\s*)" + var + R"(\s*\))", std::regex::icase),
"atomicAdd(" + var + ", uint(-1))"
);
source = std::regex_replace(source,
std::regex(R"(\batomicCounterAdd\s*\(\s*)" + var + R"(\s*,\s*([^)]+)\s*\))", std::regex::icase),
"atomicAdd(" + var + ", $1)"
);
source = std::regex_replace(source,
std::regex(R"(\batomicCounter\s*\(\s*)" + var + R"(\s*\))", std::regex::icase),
var
);
}
{
std::regex rx_barrier(R"(\batomicAdd\s*\([^;]*\);)");
source = std::regex_replace(source,
rx_barrier,
"$&\n memoryBarrierBuffer();"
);
}
source += atomicCounterEmulatedWatermark;
return true;
}
void process_sampler_buffer(std::string& source) { // a simplized version, should be rewritten in the future
if (source.find("isamplerBuffer") == std::string::npos) {
return;
@ -636,8 +698,7 @@ void inject_mg_macro_definition(std::string& glslCode) {
glslCode.insert(insertionPos, macro_definitions);
}
std::string preprocess_glsl(const std::string& glsl, GLenum shaderType) {
std::string preprocess_glsl(const std::string& glsl, GLenum shaderType, bool* atomicCounterEmulated) {
std::string ret = glsl;
// Remove lines beginning with `#line`
ret = replace_line_starting_with(ret, "#line");
@ -666,6 +727,7 @@ std::string preprocess_glsl(const std::string& glsl, GLenum shaderType) {
process_sampler_buffer(ret);
}
*atomicCounterEmulated = process_non_opaque_atomic_to_ssbo(ret);
return ret;
}
@ -783,7 +845,8 @@ std::string spirv_to_essl(std::vector<unsigned int> spirv, uint essl_version, in
static bool glslang_inited = false;
std::string GLSLtoGLSLES_2(const char *glsl_code, GLenum glsl_type, uint essl_version, int& return_code) {
std::string correct_glsl_str = preprocess_glsl(glsl_code, glsl_type);
bool atomicCounterEmulated = false;
std::string correct_glsl_str = preprocess_glsl(glsl_code, glsl_type, &atomicCounterEmulated);
LOG_D("Firstly converted GLSL:\n%s", correct_glsl_str.c_str())
int glsl_version = get_or_add_glsl_version(correct_glsl_str);
@ -814,8 +877,10 @@ std::string GLSLtoGLSLES_2(const char *glsl_code, GLenum glsl_type, uint essl_ve
essl = forceSupporterOutput(essl);
LOG_D("Originally GLSL to GLSL ES Complete: \n%s", essl.c_str())
return_code = errc;
if (return_code == 0) {
return_code = atomicCounterEmulated ? 1 : 0;
}
return essl;
}

View File

@ -16,8 +16,7 @@ extern "C" {
}
#endif
std::string getCachedESSL(const char* glsl_code, uint essl_version);
std::string GLSLtoGLSLES(const char *glsl_code, GLenum glsl_type, uint esversion, uint glsl_version);
std::string GLSLtoGLSLES(const char* glsl_code, GLenum glsl_type, uint essl_version, uint glsl_version, int& return_code);
std::string GLSLtoGLSLES_1(const char *glsl_code, GLenum glsl_type, uint esversion, int& return_code);
std::string GLSLtoGLSLES_2(const char *glsl_code, GLenum glsl_type, uint essl_version, int& return_code);
int getGLSLVersion(const char* glsl_code);

View File

@ -18,6 +18,9 @@
extern std::unordered_map<GLuint, bool> shader_map_is_sampler_buffer_emulated;
std::unordered_map<GLuint, bool> program_map_is_sampler_buffer_emulated;
extern std::unordered_map<GLuint, bool> shader_map_is_atomic_counter_emulated;
std::unordered_map<GLuint, bool> program_map_is_atomic_counter_emulated;
char* updateLayoutLocation(const char* esslSource, GLuint color, const char* name) {
std::string shaderCode(esslSource);
@ -142,6 +145,10 @@ void glAttachShader(GLuint program, GLuint shader) {
LOG_D("glAttachShader(%u, %u)", program, shader)
if (hardware->emulate_texture_buffer && shader_map_is_sampler_buffer_emulated[shader])
program_map_is_sampler_buffer_emulated[program] = true;
if (shader_map_is_atomic_counter_emulated[shader]) {
program_map_is_atomic_counter_emulated[program] = true;
LOG_D("Shader %d is atomic counter emulated, setting program %d to atomic counter emulated", shader, program)
}
GLES.glAttachShader(program, shader);
CHECK_GL_ERROR

View File

@ -19,6 +19,7 @@
struct shader_t shaderInfo;
std::unordered_map<GLuint, bool> shader_map_is_sampler_buffer_emulated;
std::unordered_map<GLuint, bool> shader_map_is_atomic_counter_emulated;
bool can_run_essl3(unsigned int esversion, const char *glsl) {
if (strncmp(glsl, "#version 100", 12) == 0) {
@ -83,9 +84,13 @@ void glShaderSource(GLuint shader, GLsizei count, const GLchar *const* string, c
LOG_D("%s", glsl_src.c_str())
GLint shaderType;
GLES.glGetShaderiv(shader, GL_SHADER_TYPE, &shaderType);
essl_src = getCachedESSL(glsl_src.c_str(), hardware->es_version);
if (essl_src.empty())
essl_src = GLSLtoGLSLES(glsl_src.c_str(), shaderType, hardware->es_version, glsl_version);
int return_code = 0;
essl_src = GLSLtoGLSLES(glsl_src.c_str(), shaderType, hardware->es_version, glsl_version, return_code);
if (return_code == 1) { //atomicCounterEmulated
shader_map_is_atomic_counter_emulated[shader] = true;
LOG_D("[INFO] [Shader] Atomic counter emulated in shader %d", shader)
}
if (essl_src.empty()) {
LOG_E("Failed to convert shader %d.", shader)
return;