mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
added opengl es option
This commit is contained in:
@@ -485,7 +485,6 @@ list(FILTER XEMU_CORE_SOURCES EXCLUDE REGEX "ui[\\\\/]gtk.*\\.c$")
|
||||
list(FILTER XEMU_CORE_SOURCES EXCLUDE REGEX "ui[\\\\/]sdl2.*\\.c$")
|
||||
list(FILTER XEMU_CORE_SOURCES EXCLUDE REGEX "ui[\\\\/]console-gl\\.c$")
|
||||
list(FILTER XEMU_CORE_SOURCES EXCLUDE REGEX "ui[\\\\/]console-vc\\.c$")
|
||||
list(FILTER XEMU_CORE_SOURCES EXCLUDE REGEX "ui[\\\\/]shader\\.c$")
|
||||
list(FILTER XEMU_CORE_SOURCES EXCLUDE REGEX "ui[\\\\/]vnc.*\\.c$")
|
||||
list(FILTER XEMU_CORE_SOURCES EXCLUDE REGEX "ui[\\\\/]xemu-snapshots\\.c$")
|
||||
list(FILTER XEMU_CORE_SOURCES EXCLUDE REGEX "ui[\\\\/]xemu-thumbnail\\.cc$")
|
||||
@@ -659,6 +658,30 @@ endif()
|
||||
file(GLOB_RECURSE QAPI_GEN_SOURCES "${QAPI_GEN_DIR}/*.c")
|
||||
list(APPEND XEMU_CORE_SOURCES ${QAPI_GEN_SOURCES})
|
||||
|
||||
# Generate GL shader headers used by ui/shader.c.
|
||||
set(XEMU_SHADER_GEN_DIR "${CMAKE_BINARY_DIR}/ui/shader")
|
||||
set(XEMU_SHADER_INPUTS
|
||||
"${REPO_ROOT}/ui/shader/texture-blit.frag"
|
||||
"${REPO_ROOT}/ui/shader/texture-blit.vert"
|
||||
"${REPO_ROOT}/ui/shader/texture-blit-flip.vert"
|
||||
)
|
||||
file(MAKE_DIRECTORY "${XEMU_SHADER_GEN_DIR}")
|
||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${XEMU_SHADER_INPUTS})
|
||||
foreach(SHADER_INPUT ${XEMU_SHADER_INPUTS})
|
||||
get_filename_component(SHADER_NAME "${SHADER_INPUT}" NAME)
|
||||
string(REPLACE "." "-" SHADER_OUTPUT_BASENAME "${SHADER_NAME}")
|
||||
set(SHADER_OUTPUT "${XEMU_SHADER_GEN_DIR}/${SHADER_OUTPUT_BASENAME}.h")
|
||||
execute_process(
|
||||
COMMAND "${PYTHON_EXECUTABLE}" "${REPO_ROOT}/scripts/shaderinclude.py" "${SHADER_INPUT}"
|
||||
OUTPUT_FILE "${SHADER_OUTPUT}"
|
||||
WORKING_DIRECTORY "${REPO_ROOT}"
|
||||
RESULT_VARIABLE SHADER_GEN_RESULT
|
||||
)
|
||||
if(NOT SHADER_GEN_RESULT EQUAL 0)
|
||||
message(FATAL_ERROR "Shader header generation failed for ${SHADER_INPUT}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Generate block coroutine wrapper sources (block-gen.c).
|
||||
set(BLOCK_GEN_C "${CMAKE_BINARY_DIR}/block-gen.c")
|
||||
execute_process(
|
||||
|
||||
@@ -577,14 +577,17 @@ cleanup:
|
||||
}
|
||||
|
||||
struct EmulatorSettings {
|
||||
int surface_scale = 1; // 1, 2, or 3
|
||||
int frame_rate_limit = 60; // 30 or 60
|
||||
int system_memory_mib = 64; // 64 or 128
|
||||
std::string tcg_thread = "multi"; // "single" or "multi"
|
||||
int surface_scale = 1; // 1, 2, or 3
|
||||
int frame_rate_limit = 60; // 30 or 60
|
||||
int system_memory_mib = 64; // 64 or 128
|
||||
std::string tcg_thread = "multi"; // "single" or "multi"
|
||||
std::string renderer = "vulkan"; // "vulkan" or "opengl"
|
||||
std::string filtering = "linear"; // "linear" or "nearest"
|
||||
bool use_dsp = false;
|
||||
bool hrtf = true;
|
||||
bool cache_shaders = true;
|
||||
bool hard_fpu = true;
|
||||
bool vsync = false;
|
||||
bool skip_boot_anim = false;
|
||||
};
|
||||
|
||||
@@ -645,13 +648,13 @@ static bool WriteConfigToml(const std::string& config_path,
|
||||
|
||||
general->insert_or_assign("show_welcome", false);
|
||||
general->insert_or_assign("skip_boot_anim", settings.skip_boot_anim);
|
||||
display->insert_or_assign("renderer", "vulkan");
|
||||
if (!display->contains("filtering")) {
|
||||
display->insert_or_assign("filtering", "nearest");
|
||||
}
|
||||
if (!display_window->contains("vsync")) {
|
||||
display_window->insert_or_assign("vsync", false);
|
||||
{
|
||||
const std::string r = (settings.renderer == "opengl") ? "opengl" : "vulkan";
|
||||
display->insert_or_assign("renderer", r);
|
||||
}
|
||||
display->insert_or_assign(
|
||||
"filtering", settings.filtering == "nearest" ? "nearest" : "linear");
|
||||
display_window->insert_or_assign("vsync", settings.vsync);
|
||||
// User-controlled settings (always written so they take effect immediately)
|
||||
{
|
||||
int scale = settings.surface_scale;
|
||||
@@ -678,9 +681,6 @@ static bool WriteConfigToml(const std::string& config_path,
|
||||
if (!audio->contains("volume_limit")) {
|
||||
audio->insert_or_assign("volume_limit", 1.0);
|
||||
}
|
||||
if (!android->contains("force_cpu_blit")) {
|
||||
android->insert_or_assign("force_cpu_blit", false);
|
||||
}
|
||||
if (!android->contains("tcg_tuning")) {
|
||||
android->insert_or_assign("tcg_tuning", true);
|
||||
}
|
||||
@@ -830,6 +830,19 @@ static SetupFiles SyncSetupFiles() {
|
||||
emuSettings.tcg_thread = "single";
|
||||
}
|
||||
}
|
||||
{
|
||||
std::string rendererPref = GetPrefString(env, activity, "setting_renderer");
|
||||
if (rendererPref == "opengl") {
|
||||
emuSettings.renderer = "opengl";
|
||||
}
|
||||
}
|
||||
{
|
||||
std::string filteringPref = GetPrefString(env, activity, "setting_filtering");
|
||||
if (filteringPref == "nearest") {
|
||||
emuSettings.filtering = "nearest";
|
||||
}
|
||||
}
|
||||
emuSettings.vsync = GetPrefBool(env, activity, "setting_vsync", false);
|
||||
out.audio_driver = GetPrefString(env, activity, "setting_audio_driver");
|
||||
|
||||
int displayMode = GetPrefInt(env, activity, "setting_display_mode", 0);
|
||||
@@ -988,12 +1001,13 @@ extern "C" int SDL_main(int argc, char* argv[]) {
|
||||
} else if (!g_config.sys.files.eeprom_path) {
|
||||
xemu_settings_set_string(&g_config.sys.files.eeprom_path, "");
|
||||
}
|
||||
setenv("XEMU_ANDROID_FORCE_CPU_BLIT", "0", 1);
|
||||
g_config.general.show_welcome = false;
|
||||
g_config.perf.cache_shaders = true;
|
||||
LogInfoInt("Config final show_welcome=%d", g_config.general.show_welcome ? 1 : 0);
|
||||
LogInfoInt("Config final cache_shaders=%d", g_config.perf.cache_shaders ? 1 : 0);
|
||||
LogInfoInt("Config final renderer=%d", (int)g_config.display.renderer);
|
||||
LogInfoFmt("Config final renderer=%s",
|
||||
(g_config.display.renderer == 0) ? "Vulkan" :
|
||||
(g_config.display.renderer == 1) ? "OpenGL" : "Null");
|
||||
LogInfoFmt("Config final bootrom=%s", g_config.sys.files.bootrom_path ? g_config.sys.files.bootrom_path : "(null)");
|
||||
LogInfoFmt("Config final flashrom=%s", g_config.sys.files.flashrom_path ? g_config.sys.files.flashrom_path : "(null)");
|
||||
LogInfoFmt("Config final hdd=%s", g_config.sys.files.hdd_path ? g_config.sys.files.hdd_path : "(null)");
|
||||
|
||||
@@ -68,7 +68,7 @@ static void xemu_settings_apply_defaults(void)
|
||||
g_config.input.keyboard_controller_scancode_map.rtrigger = 18;
|
||||
|
||||
g_config.display.renderer = CONFIG_DISPLAY_RENDERER_VULKAN;
|
||||
g_config.display.filtering = CONFIG_DISPLAY_FILTERING_NEAREST;
|
||||
g_config.display.filtering = CONFIG_DISPLAY_FILTERING_LINEAR;
|
||||
g_config.display.quality.surface_scale = 1;
|
||||
g_config.display.window.fullscreen_on_startup = false;
|
||||
g_config.display.window.fullscreen_exclusive = false;
|
||||
@@ -209,7 +209,6 @@ bool xemu_settings_load(void)
|
||||
|
||||
xemu_settings_apply_defaults();
|
||||
error_msg.clear();
|
||||
setenv("XEMU_ANDROID_FORCE_CPU_BLIT", "0", 1);
|
||||
setenv("XEMU_ANDROID_TCG_TUNING", "1", 1);
|
||||
setenv("XEMU_ANDROID_TCG_THREAD", "multi", 1);
|
||||
setenv("XEMU_ANDROID_TCG_TB_SIZE", "128", 1);
|
||||
@@ -247,13 +246,17 @@ bool xemu_settings_load(void)
|
||||
g_config.general.skip_boot_anim = *skip_boot_anim;
|
||||
}
|
||||
|
||||
// Display settings - force Vulkan on Android
|
||||
g_config.display.renderer = CONFIG_DISPLAY_RENDERER_VULKAN;
|
||||
// Display settings
|
||||
if (auto renderer = display["renderer"].value<std::string>()) {
|
||||
CONFIG_DISPLAY_RENDERER parsed;
|
||||
if (parse_renderer(*renderer, &parsed) && parsed != CONFIG_DISPLAY_RENDERER_VULKAN) {
|
||||
if (parse_renderer(*renderer, &parsed)) {
|
||||
g_config.display.renderer = parsed;
|
||||
__android_log_print(ANDROID_LOG_INFO, "xemu-android",
|
||||
"Config display.renderer=%s (%d)",
|
||||
renderer->c_str(), (int)parsed);
|
||||
} else {
|
||||
__android_log_print(ANDROID_LOG_WARN, "xemu-android",
|
||||
"Config display.renderer=%s requested, but Android forces Vulkan",
|
||||
"Config display.renderer=%s unknown, using default",
|
||||
renderer->c_str());
|
||||
}
|
||||
}
|
||||
@@ -311,9 +314,6 @@ bool xemu_settings_load(void)
|
||||
}
|
||||
|
||||
// Android-specific settings
|
||||
if (auto force_cpu_blit = android_cfg["force_cpu_blit"].value<bool>()) {
|
||||
setenv("XEMU_ANDROID_FORCE_CPU_BLIT", *force_cpu_blit ? "1" : "0", 1);
|
||||
}
|
||||
if (auto egl_offscreen = android_cfg["egl_offscreen"].value<bool>()) {
|
||||
if (!*egl_offscreen) {
|
||||
setenv("XEMU_ANDROID_EGL_OFFSCREEN", "0", 1);
|
||||
|
||||
@@ -79,6 +79,8 @@ class SettingsActivity : AppCompatActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_settings)
|
||||
|
||||
val toggleGraphicsApi = findViewById<MaterialButtonToggleGroup>(R.id.toggle_graphics_api)
|
||||
val toggleFiltering = findViewById<MaterialButtonToggleGroup>(R.id.toggle_filtering)
|
||||
val toggleScale = findViewById<MaterialButtonToggleGroup>(R.id.toggle_resolution_scale)
|
||||
val btn1x = findViewById<MaterialButton>(R.id.btn_scale_1x)
|
||||
val btn2x = findViewById<MaterialButton>(R.id.btn_scale_2x)
|
||||
@@ -93,6 +95,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||
val switchHrtf = findViewById<MaterialSwitch>(R.id.switch_hrtf)
|
||||
val switchShaders = findViewById<MaterialSwitch>(R.id.switch_cache_shaders)
|
||||
val switchFpu = findViewById<MaterialSwitch>(R.id.switch_hard_fpu)
|
||||
val switchVsync = findViewById<MaterialSwitch>(R.id.switch_vsync)
|
||||
val switchSkipBootAnim = findViewById<MaterialSwitch>(R.id.switch_skip_boot_anim)
|
||||
val toggleAudioDriver = findViewById<MaterialButtonToggleGroup>(R.id.toggle_audio_driver)
|
||||
val btnSave = findViewById<MaterialButton>(R.id.btn_settings_save)
|
||||
@@ -106,6 +109,20 @@ class SettingsActivity : AppCompatActivity() {
|
||||
dropdownEepromVideoStandard = findViewById(R.id.dropdown_eeprom_video_standard)
|
||||
|
||||
// Load current values
|
||||
val renderer = prefs.getString("setting_renderer", "vulkan") ?: "vulkan"
|
||||
if (renderer == "opengl") {
|
||||
toggleGraphicsApi.check(R.id.btn_renderer_opengl)
|
||||
} else {
|
||||
toggleGraphicsApi.check(R.id.btn_renderer_vulkan)
|
||||
}
|
||||
|
||||
val filtering = prefs.getString("setting_filtering", "linear") ?: "linear"
|
||||
if (filtering == "nearest") {
|
||||
toggleFiltering.check(R.id.btn_filtering_nearest)
|
||||
} else {
|
||||
toggleFiltering.check(R.id.btn_filtering_linear)
|
||||
}
|
||||
|
||||
val scale = prefs.getInt("setting_surface_scale", 1)
|
||||
when (scale) {
|
||||
2 -> toggleScale.check(R.id.btn_scale_2x)
|
||||
@@ -147,6 +164,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||
switchHrtf.isChecked = prefs.getBoolean("setting_hrtf", true)
|
||||
switchShaders.isChecked = prefs.getBoolean("setting_cache_shaders", true)
|
||||
switchFpu.isChecked = prefs.getBoolean("setting_hard_fpu", true)
|
||||
switchVsync.isChecked = prefs.getBoolean("setting_vsync", false)
|
||||
switchSkipBootAnim.isChecked =
|
||||
prefs.getBoolean("setting_skip_boot_anim", false)
|
||||
|
||||
@@ -199,6 +217,14 @@ class SettingsActivity : AppCompatActivity() {
|
||||
R.id.btn_audio_disabled -> "dummy"
|
||||
else -> "openslES"
|
||||
}
|
||||
val selectedRenderer = when (toggleGraphicsApi.checkedButtonId) {
|
||||
R.id.btn_renderer_opengl -> "opengl"
|
||||
else -> "vulkan"
|
||||
}
|
||||
val selectedFiltering = when (toggleFiltering.checkedButtonId) {
|
||||
R.id.btn_filtering_nearest -> "nearest"
|
||||
else -> "linear"
|
||||
}
|
||||
|
||||
val edit = prefs.edit()
|
||||
.putInt("setting_display_mode", selectedDisplayMode)
|
||||
@@ -210,8 +236,11 @@ class SettingsActivity : AppCompatActivity() {
|
||||
.putBoolean("setting_hrtf", switchHrtf.isChecked)
|
||||
.putBoolean("setting_cache_shaders", switchShaders.isChecked)
|
||||
.putBoolean("setting_hard_fpu", switchFpu.isChecked)
|
||||
.putBoolean("setting_vsync", switchVsync.isChecked)
|
||||
.putBoolean("setting_skip_boot_anim", switchSkipBootAnim.isChecked)
|
||||
.putString("setting_audio_driver", selectedAudioDriver)
|
||||
.putString("setting_filtering", selectedFiltering)
|
||||
.putString("setting_renderer", selectedRenderer)
|
||||
|
||||
when {
|
||||
clearVulkan -> edit
|
||||
|
||||
@@ -55,6 +55,82 @@
|
||||
android:textAppearance="@style/TextAppearance.Material3.LabelLarge"
|
||||
android:textColor="@color/xemu_green" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:text="@string/settings_graphics_api"
|
||||
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
|
||||
android:textColor="@color/xemu_text_muted" />
|
||||
|
||||
<com.google.android.material.button.MaterialButtonToggleGroup
|
||||
android:id="@+id/toggle_graphics_api"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:selectionRequired="true"
|
||||
app:singleSelection="true">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_renderer_vulkan"
|
||||
style="?attr/materialButtonOutlinedStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/settings_graphics_api_vulkan" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_renderer_opengl"
|
||||
style="?attr/materialButtonOutlinedStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/settings_graphics_api_opengl" />
|
||||
|
||||
</com.google.android.material.button.MaterialButtonToggleGroup>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:text="@string/settings_filtering"
|
||||
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
|
||||
android:textColor="@color/xemu_text_muted" />
|
||||
|
||||
<com.google.android.material.button.MaterialButtonToggleGroup
|
||||
android:id="@+id/toggle_filtering"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
app:selectionRequired="true"
|
||||
app:singleSelection="true">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_filtering_linear"
|
||||
style="?attr/materialButtonOutlinedStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/settings_filtering_linear" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_filtering_nearest"
|
||||
style="?attr/materialButtonOutlinedStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/settings_filtering_nearest" />
|
||||
|
||||
</com.google.android.material.button.MaterialButtonToggleGroup>
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/switch_vsync"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/settings_vsync"
|
||||
android:textColor="@color/xemu_text_muted" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -80,6 +80,13 @@
|
||||
<string name="settings_save">Save</string>
|
||||
|
||||
<string name="settings_section_display">Display</string>
|
||||
<string name="settings_graphics_api">Graphics API</string>
|
||||
<string name="settings_graphics_api_vulkan">Vulkan</string>
|
||||
<string name="settings_graphics_api_opengl">OpenGL ES</string>
|
||||
<string name="settings_filtering">Display Filtering</string>
|
||||
<string name="settings_filtering_linear">Linear</string>
|
||||
<string name="settings_filtering_nearest">Nearest</string>
|
||||
<string name="settings_vsync">VSync</string>
|
||||
<string name="settings_section_vulkan_driver">Custom Vulkan Driver</string>
|
||||
<string name="settings_vulkan_driver_none">System Default</string>
|
||||
<string name="settings_vulkan_driver_browse">Browse…</string>
|
||||
|
||||
@@ -25,8 +25,6 @@ void nv2a_init(PCIBus *bus, int devfn, MemoryRegion *ram);
|
||||
void nv2a_context_init(void);
|
||||
#ifdef __ANDROID__
|
||||
void nv2a_android_early_context_init(void);
|
||||
bool nv2a_android_copy_readback(uint8_t **buffer, size_t *buffer_size,
|
||||
int *width, int *height);
|
||||
#endif
|
||||
int nv2a_get_framebuffer_surface(void);
|
||||
void nv2a_release_framebuffer_surface(void);
|
||||
|
||||
+102
-214
@@ -26,180 +26,110 @@
|
||||
#include "renderer.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <android/log.h>
|
||||
#include "qemu/thread.h"
|
||||
#include <string.h>
|
||||
#include "system/memory.h"
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
static void check_gl_error_android(const char *context)
|
||||
static void gl_log_errors(const char *ctx)
|
||||
{
|
||||
GLenum err;
|
||||
int count = 0;
|
||||
while ((err = glGetError()) != GL_NO_ERROR) {
|
||||
fprintf(stderr, "GL error 0x%X in %s\n", err, context);
|
||||
if (++count >= 10) {
|
||||
break;
|
||||
}
|
||||
GLenum gl_err;
|
||||
|
||||
while ((gl_err = glGetError()) != GL_NO_ERROR) {
|
||||
__android_log_print(ANDROID_LOG_WARN, "xemu-android",
|
||||
"GL error 0x%X at %s", gl_err, ctx);
|
||||
}
|
||||
}
|
||||
#define GL_ASSERT_NO_ERROR(context) check_gl_error_android(context)
|
||||
|
||||
#define GL_ASSERT_NO_ERROR(ctx) gl_log_errors(ctx)
|
||||
#else
|
||||
#define GL_ASSERT_NO_ERROR(context) assert(glGetError() == GL_NO_ERROR)
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
static QemuMutex g_android_readback_mutex;
|
||||
static bool g_android_readback_mutex_init = false;
|
||||
static uint8_t *g_android_readback_buf = NULL;
|
||||
static size_t g_android_readback_size = 0;
|
||||
static int g_android_readback_w = 0;
|
||||
static int g_android_readback_h = 0;
|
||||
static bool g_android_readback_ready = false;
|
||||
typedef struct AndroidDisplayGLState {
|
||||
GLint framebuffer;
|
||||
GLint program;
|
||||
GLint vertex_array;
|
||||
GLint array_buffer;
|
||||
GLint active_texture;
|
||||
GLint texture_2d_unit0;
|
||||
GLint viewport[4];
|
||||
GLboolean color_mask[4];
|
||||
GLfloat clear_color[4];
|
||||
GLboolean scissor_test;
|
||||
GLboolean blend;
|
||||
GLboolean stencil_test;
|
||||
GLboolean cull_face;
|
||||
GLboolean depth_test;
|
||||
} AndroidDisplayGLState;
|
||||
|
||||
static bool android_force_cpu_blit(void)
|
||||
static void android_display_capture_gl_state(AndroidDisplayGLState *state)
|
||||
{
|
||||
static int cached = -1;
|
||||
if (cached < 0) {
|
||||
const char *env = getenv("XEMU_ANDROID_FORCE_CPU_BLIT");
|
||||
if (!env) {
|
||||
cached = 1;
|
||||
} else if (!strcmp(env, "1") || !strcmp(env, "true") ||
|
||||
!strcmp(env, "TRUE")) {
|
||||
cached = 1;
|
||||
} else {
|
||||
cached = 0;
|
||||
}
|
||||
}
|
||||
return cached == 1;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &state->framebuffer);
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, &state->program);
|
||||
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &state->vertex_array);
|
||||
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &state->array_buffer);
|
||||
glGetIntegerv(GL_ACTIVE_TEXTURE, &state->active_texture);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &state->texture_2d_unit0);
|
||||
glActiveTexture(state->active_texture);
|
||||
glGetIntegerv(GL_VIEWPORT, state->viewport);
|
||||
glGetBooleanv(GL_COLOR_WRITEMASK, state->color_mask);
|
||||
glGetFloatv(GL_COLOR_CLEAR_VALUE, state->clear_color);
|
||||
state->scissor_test = glIsEnabled(GL_SCISSOR_TEST);
|
||||
state->blend = glIsEnabled(GL_BLEND);
|
||||
state->stencil_test = glIsEnabled(GL_STENCIL_TEST);
|
||||
state->cull_face = glIsEnabled(GL_CULL_FACE);
|
||||
state->depth_test = glIsEnabled(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
static bool android_readback_is_black(const uint8_t *buf, size_t size,
|
||||
int width, int height)
|
||||
static void android_display_restore_gl_state(
|
||||
const AndroidDisplayGLState *state)
|
||||
{
|
||||
if (!buf || size < 4 || width <= 0 || height <= 0) {
|
||||
return false;
|
||||
}
|
||||
size_t tl_off = 0;
|
||||
size_t mid_off = ((size_t)(height / 2) * (size_t)width +
|
||||
(size_t)(width / 2)) * 4;
|
||||
size_t br_off = ((size_t)(height - 1) * (size_t)width +
|
||||
(size_t)(width - 1)) * 4;
|
||||
uint32_t tl = 0, mid = 0, br = 0;
|
||||
if (tl_off + 4 <= size) {
|
||||
memcpy(&tl, buf + tl_off, sizeof(tl));
|
||||
}
|
||||
if (mid_off + 4 <= size) {
|
||||
memcpy(&mid, buf + mid_off, sizeof(mid));
|
||||
}
|
||||
if (br_off + 4 <= size) {
|
||||
memcpy(&br, buf + br_off, sizeof(br));
|
||||
}
|
||||
bool black_tl = (tl == 0x00000000u || tl == 0xff000000u);
|
||||
bool black_mid = (mid == 0x00000000u || mid == 0xff000000u);
|
||||
bool black_br = (br == 0x00000000u || br == 0xff000000u);
|
||||
return black_tl && black_mid && black_br;
|
||||
}
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, state->framebuffer);
|
||||
glUseProgram(state->program);
|
||||
glBindVertexArray(state->vertex_array);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, state->array_buffer);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, state->texture_2d_unit0);
|
||||
glActiveTexture(state->active_texture);
|
||||
glViewport(state->viewport[0], state->viewport[1],
|
||||
state->viewport[2], state->viewport[3]);
|
||||
glColorMask(state->color_mask[0], state->color_mask[1],
|
||||
state->color_mask[2], state->color_mask[3]);
|
||||
glClearColor(state->clear_color[0], state->clear_color[1],
|
||||
state->clear_color[2], state->clear_color[3]);
|
||||
|
||||
static void android_store_readback(NV2AState *d, SurfaceBinding *surface,
|
||||
int width, int height)
|
||||
{
|
||||
if (width <= 0 || height <= 0) {
|
||||
return;
|
||||
if (state->scissor_test) {
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
} else {
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
if (!g_android_readback_mutex_init) {
|
||||
qemu_mutex_init(&g_android_readback_mutex);
|
||||
g_android_readback_mutex_init = true;
|
||||
if (state->blend) {
|
||||
glEnable(GL_BLEND);
|
||||
} else {
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
size_t needed = (size_t)width * (size_t)height * 4;
|
||||
qemu_mutex_lock(&g_android_readback_mutex);
|
||||
if (needed > g_android_readback_size) {
|
||||
g_android_readback_buf = g_realloc(g_android_readback_buf, needed);
|
||||
g_android_readback_size = needed;
|
||||
if (state->stencil_test) {
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
} else {
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
}
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
g_android_readback_buf);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
||||
bool display_black = android_readback_is_black(
|
||||
g_android_readback_buf, g_android_readback_size, width, height);
|
||||
bool used_surface = false;
|
||||
if (display_black && d && surface && surface->gl_buffer) {
|
||||
PGRAPHState *pg = &d->pgraph;
|
||||
PGRAPHGLState *r = pg->gl_renderer_state;
|
||||
unsigned int surf_w = surface->width;
|
||||
unsigned int surf_h = surface->height;
|
||||
pgraph_apply_scaling_factor(pg, &surf_w, &surf_h);
|
||||
if (surf_w > 0 && surf_h > 0) {
|
||||
size_t surf_needed = (size_t)surf_w * (size_t)surf_h * 4;
|
||||
if (surf_needed > g_android_readback_size) {
|
||||
g_android_readback_buf = g_realloc(g_android_readback_buf,
|
||||
surf_needed);
|
||||
g_android_readback_size = surf_needed;
|
||||
}
|
||||
GLint prev_fbo = 0;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prev_fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, r->disp_rndr.fbo);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D, surface->gl_buffer, 0);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glReadPixels(0, 0, surf_w, surf_h, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
g_android_readback_buf);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D, r->gl_display_buffer, 0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, prev_fbo);
|
||||
g_android_readback_w = (int)surf_w;
|
||||
g_android_readback_h = (int)surf_h;
|
||||
used_surface = true;
|
||||
static unsigned int surf_log = 0;
|
||||
if ((surf_log++ % 120) == 0) {
|
||||
__android_log_print(ANDROID_LOG_WARN, "xemu-android",
|
||||
"android readback: display black, using surface vram=0x%x size=%ux%u",
|
||||
(unsigned)surface->vram_addr,
|
||||
surf_w, surf_h);
|
||||
}
|
||||
}
|
||||
if (state->cull_face) {
|
||||
glEnable(GL_CULL_FACE);
|
||||
} else {
|
||||
glDisable(GL_CULL_FACE);
|
||||
}
|
||||
if (!used_surface) {
|
||||
g_android_readback_w = width;
|
||||
g_android_readback_h = height;
|
||||
if (state->depth_test) {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
} else {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
}
|
||||
g_android_readback_ready = true;
|
||||
qemu_mutex_unlock(&g_android_readback_mutex);
|
||||
}
|
||||
|
||||
bool nv2a_android_copy_readback(uint8_t **buffer, size_t *buffer_size,
|
||||
int *width, int *height)
|
||||
{
|
||||
if (!android_force_cpu_blit()) {
|
||||
return false;
|
||||
}
|
||||
if (!g_android_readback_mutex_init || !g_android_readback_ready) {
|
||||
return false;
|
||||
}
|
||||
qemu_mutex_lock(&g_android_readback_mutex);
|
||||
size_t needed = (size_t)g_android_readback_w * (size_t)g_android_readback_h * 4;
|
||||
if (!g_android_readback_buf || needed == 0) {
|
||||
qemu_mutex_unlock(&g_android_readback_mutex);
|
||||
return false;
|
||||
}
|
||||
if (*buffer_size < needed) {
|
||||
*buffer = g_realloc(*buffer, needed);
|
||||
*buffer_size = needed;
|
||||
}
|
||||
memcpy(*buffer, g_android_readback_buf, needed);
|
||||
*width = g_android_readback_w;
|
||||
*height = g_android_readback_h;
|
||||
qemu_mutex_unlock(&g_android_readback_mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void pgraph_gl_init_display(NV2AState *d)
|
||||
{
|
||||
struct PGRAPHState *pg = &d->pgraph;
|
||||
@@ -556,16 +486,6 @@ static void render_display(NV2AState *d, SurfaceBinding *surface)
|
||||
glDrawBuffers(1, DrawBuffers);
|
||||
GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (fb_status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
#ifdef __ANDROID__
|
||||
__android_log_print(ANDROID_LOG_ERROR, "xemu-android",
|
||||
"render_display: framebuffer incomplete 0x%X (fmt=%d ifmt=%d type=%d size=%ux%u)",
|
||||
fb_status,
|
||||
r->gl_display_buffer_format,
|
||||
r->gl_display_buffer_internal_format,
|
||||
r->gl_display_buffer_type,
|
||||
r->gl_display_buffer_width,
|
||||
r->gl_display_buffer_height);
|
||||
#else
|
||||
fprintf(stderr,
|
||||
"render_display: framebuffer incomplete 0x%X (fmt=%d ifmt=%d type=%d size=%ux%u)\n",
|
||||
fb_status,
|
||||
@@ -574,7 +494,6 @@ static void render_display(NV2AState *d, SurfaceBinding *surface)
|
||||
r->gl_display_buffer_type,
|
||||
r->gl_display_buffer_width,
|
||||
r->gl_display_buffer_height);
|
||||
#endif
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D, 0, 0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
@@ -582,44 +501,13 @@ static void render_display(NV2AState *d, SurfaceBinding *surface)
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, surface->gl_buffer);
|
||||
#ifdef __ANDROID__
|
||||
bool clip_enable = false;
|
||||
float clip_x = 0.0f;
|
||||
float clip_y = 0.0f;
|
||||
float clip_w = 0.0f;
|
||||
float clip_h = 0.0f;
|
||||
if (android_force_cpu_blit()) {
|
||||
clip_x = (float)surface->shape.clip_x;
|
||||
clip_y = (float)surface->shape.clip_y;
|
||||
clip_w = (float)surface->shape.clip_width;
|
||||
clip_h = (float)surface->shape.clip_height;
|
||||
if (clip_w > 0.0f && clip_h > 0.0f) {
|
||||
clip_enable = true;
|
||||
static unsigned int clip_log = 0;
|
||||
if ((clip_log++ % 120) == 0) {
|
||||
GLint tex_w = 0;
|
||||
GLint tex_h = 0;
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &tex_w);
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &tex_h);
|
||||
__android_log_print(ANDROID_LOG_WARN, "xemu-android",
|
||||
"render_display: clip rect x=%g y=%g w=%g h=%g tex=%dx%d disp=%ux%u",
|
||||
clip_x, clip_y, clip_w, clip_h,
|
||||
(int)tex_w, (int)tex_h,
|
||||
width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
glBindVertexArray(r->disp_rndr.vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, r->disp_rndr.vbo);
|
||||
glUseProgram(r->disp_rndr.prog);
|
||||
glProgramUniform1i(r->disp_rndr.prog, r->disp_rndr.tex_loc, 0);
|
||||
glUniform2f(r->disp_rndr.display_size_loc, width, height);
|
||||
glUniform1f(r->disp_rndr.line_offset_loc, line_offset);
|
||||
#ifdef __ANDROID__
|
||||
glUniform1i(r->disp_rndr.clip_enable_loc, clip_enable ? 1 : 0);
|
||||
glUniform4f(r->disp_rndr.clip_rect_loc, clip_x, clip_y, clip_w, clip_h);
|
||||
#endif
|
||||
glUniform1i(r->disp_rndr.clip_enable_loc, 0);
|
||||
render_display_pvideo_overlay(d);
|
||||
|
||||
glViewport(0, 0, width, height);
|
||||
@@ -629,28 +517,30 @@ static void render_display(NV2AState *d, SurfaceBinding *surface)
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
#ifndef __ANDROID__ /* glPolygonMode not available in GLES 3.x core */
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
#endif
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
if (android_force_cpu_blit()) {
|
||||
android_store_readback(d, surface, width, height);
|
||||
}
|
||||
#endif
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D, 0, 0);
|
||||
}
|
||||
|
||||
static void gl_fence(void)
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
/* Shared-context sync objects are still producing invalid-operation
|
||||
* failures on Adreno. Favor correctness over throughput here. */
|
||||
glFinish();
|
||||
#else
|
||||
GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
int result = glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT,
|
||||
(GLuint64)(5000000000));
|
||||
assert(result == GL_CONDITION_SATISFIED || result == GL_ALREADY_SIGNALED);
|
||||
glDeleteSync(fence);
|
||||
#endif
|
||||
}
|
||||
|
||||
void pgraph_gl_sync(NV2AState *d)
|
||||
@@ -677,29 +567,27 @@ void pgraph_gl_sync(NV2AState *d)
|
||||
|
||||
/* Wait for queued commands to complete */
|
||||
#ifdef __ANDROID__
|
||||
bool force_upload = !tcg_enabled();
|
||||
if (android_force_cpu_blit() && surface->draw_time == 0) {
|
||||
force_upload = true;
|
||||
static unsigned int no_draw_log = 0;
|
||||
if ((no_draw_log++ % 120) == 0) {
|
||||
__android_log_print(ANDROID_LOG_WARN, "xemu-android",
|
||||
"pgraph_gl_sync: forcing upload (no draws yet) vram=0x%x",
|
||||
(unsigned)surface->vram_addr);
|
||||
}
|
||||
}
|
||||
pgraph_gl_upload_surface_data(d, surface, force_upload);
|
||||
#else
|
||||
pgraph_gl_upload_surface_data(d, surface, !tcg_enabled());
|
||||
GL_ASSERT_NO_ERROR("pgraph_gl_sync: pre-upload");
|
||||
#endif
|
||||
pgraph_gl_upload_surface_data(d, surface, !tcg_enabled());
|
||||
GL_ASSERT_NO_ERROR("pgraph_gl_sync: surface upload");
|
||||
gl_fence();
|
||||
GL_ASSERT_NO_ERROR("pgraph_gl_sync: upload fence");
|
||||
GL_ASSERT_NO_ERROR("pgraph_gl_sync: upload finish");
|
||||
|
||||
/* Render framebuffer */
|
||||
#ifdef __ANDROID__
|
||||
glo_set_current(g_nv2a_context_render);
|
||||
render_display(d, surface);
|
||||
gl_fence();
|
||||
GL_ASSERT_NO_ERROR("pgraph_gl_sync: render fence");
|
||||
{
|
||||
AndroidDisplayGLState state;
|
||||
|
||||
glo_set_current(g_nv2a_context_render);
|
||||
GL_ASSERT_NO_ERROR("pgraph_gl_sync: pre-render");
|
||||
android_display_capture_gl_state(&state);
|
||||
render_display(d, surface);
|
||||
GL_ASSERT_NO_ERROR("pgraph_gl_sync: render display");
|
||||
android_display_restore_gl_state(&state);
|
||||
gl_fence();
|
||||
GL_ASSERT_NO_ERROR("pgraph_gl_sync: render finish");
|
||||
}
|
||||
#else
|
||||
/* Render framebuffer in display context */
|
||||
glo_set_current(g_nv2a_context_display);
|
||||
|
||||
@@ -163,7 +163,6 @@ void pgraph_gl_draw_begin(NV2AState *d)
|
||||
assert(r->color_binding || r->zeta_binding);
|
||||
|
||||
pgraph_gl_bind_textures(d);
|
||||
pgraph_gl_bind_shaders(pg);
|
||||
|
||||
glColorMask(mask_red, mask_green, mask_blue, mask_alpha);
|
||||
glDepthMask(!!(control_0 & NV_PGRAPH_CONTROL_0_ZWRITEENABLE));
|
||||
@@ -215,8 +214,10 @@ void pgraph_gl_draw_begin(NV2AState *d)
|
||||
|
||||
/* Polygon offset is handled in geometry and fragment shaders explicitly */
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
#ifndef __ANDROID__ /* GL_POLYGON_OFFSET_LINE/POINT not valid in GLES 3.x */
|
||||
glDisable(GL_POLYGON_OFFSET_LINE);
|
||||
glDisable(GL_POLYGON_OFFSET_POINT);
|
||||
#endif
|
||||
|
||||
/* Depth testing */
|
||||
if (depth_test) {
|
||||
@@ -234,8 +235,10 @@ void pgraph_gl_draw_begin(NV2AState *d)
|
||||
glEnable(GL_DEPTH_CLAMP);
|
||||
#endif
|
||||
|
||||
#ifndef __ANDROID__ /* glProvokingVertex not available in GLES 3.x */
|
||||
/* Set first vertex convention to match Vulkan default */
|
||||
glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
|
||||
#endif
|
||||
|
||||
if (stencil_test) {
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
@@ -281,7 +284,11 @@ void pgraph_gl_draw_begin(NV2AState *d)
|
||||
glDisable(GL_DITHER);
|
||||
}
|
||||
|
||||
#ifndef __ANDROID__
|
||||
/* GL_PROGRAM_POINT_SIZE is not a valid enum in GLES 3.x;
|
||||
* point size from gl_PointSize is always enabled in GLES. */
|
||||
glEnable(GL_PROGRAM_POINT_SIZE);
|
||||
#endif
|
||||
|
||||
bool anti_aliasing = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_ANTIALIASING), NV_PGRAPH_ANTIALIASING_ENABLE);
|
||||
|
||||
@@ -328,6 +335,9 @@ void pgraph_gl_draw_begin(NV2AState *d)
|
||||
glScissor(xmin, ymin, scissor_width, scissor_height);
|
||||
|
||||
/* Visibility testing */
|
||||
/* GL_SAMPLES_PASSED is desktop-only; GLES has GL_ANY_SAMPLES_PASSED but
|
||||
* only returns boolean. Skip occlusion queries entirely on Android. */
|
||||
#ifndef __ANDROID__
|
||||
if (pg->zpass_pixel_count_enable) {
|
||||
r->gl_zpass_pixel_count_query_count++;
|
||||
r->gl_zpass_pixel_count_queries = (GLuint*)g_realloc(
|
||||
@@ -340,6 +350,7 @@ void pgraph_gl_draw_begin(NV2AState *d)
|
||||
r->gl_zpass_pixel_count_query_count - 1] = gl_query;
|
||||
glBeginQuery(GL_SAMPLES_PASSED, gl_query);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void pgraph_gl_draw_end(NV2AState *d)
|
||||
@@ -373,10 +384,12 @@ void pgraph_gl_draw_end(NV2AState *d)
|
||||
pgraph_gl_flush_draw(d);
|
||||
|
||||
/* End of visibility testing */
|
||||
#ifndef __ANDROID__
|
||||
if (pg->zpass_pixel_count_enable) {
|
||||
nv2a_profile_inc_counter(NV2A_PROF_QUERY);
|
||||
glEndQuery(GL_SAMPLES_PASSED);
|
||||
}
|
||||
#endif
|
||||
|
||||
pg->draw_time++;
|
||||
#ifdef __ANDROID__
|
||||
@@ -400,7 +413,6 @@ void pgraph_gl_flush_draw(NV2AState *d)
|
||||
if (!(r->color_binding || r->zeta_binding)) {
|
||||
return;
|
||||
}
|
||||
assert(r->shader_binding);
|
||||
|
||||
PrimAssemblyState assembly = {
|
||||
.primitive_mode = pg->primitive_mode,
|
||||
@@ -426,6 +438,7 @@ void pgraph_gl_flush_draw(NV2AState *d)
|
||||
pg->draw_arrays_max_count - 1,
|
||||
false, 0,
|
||||
pg->draw_arrays_max_count - 1);
|
||||
pgraph_gl_bind_shaders(pg);
|
||||
|
||||
PrimRewrite prim_rw = pgraph_prim_rewrite_ranges(
|
||||
&r->prim_rewrite_buf, assembly, pg->draw_arrays_start,
|
||||
@@ -469,6 +482,7 @@ void pgraph_gl_flush_draw(NV2AState *d)
|
||||
pgraph_gl_bind_vertex_attributes(
|
||||
d, min_element, max_element, false, 0,
|
||||
draw_indices[draw_index_count - 1]);
|
||||
pgraph_gl_bind_shaders(pg);
|
||||
|
||||
if (prim_rw.num_indices > 0) {
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, r->gl_prim_rewrite_buffer);
|
||||
@@ -508,10 +522,12 @@ void pgraph_gl_flush_draw(NV2AState *d)
|
||||
nv2a_profile_inc_counter(NV2A_PROF_INLINE_BUFFERS);
|
||||
assert(pg->inline_array_length == 0);
|
||||
|
||||
if (pg->compressed_attrs) {
|
||||
if (pg->compressed_attrs || pg->swizzle_attrs || pg->uniform_attrs) {
|
||||
pg->compressed_attrs = 0;
|
||||
pgraph_gl_bind_shaders(pg);
|
||||
pg->uniform_attrs = 0;
|
||||
pg->swizzle_attrs = 0;
|
||||
}
|
||||
pgraph_gl_bind_shaders(pg);
|
||||
|
||||
for (int i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) {
|
||||
VertexAttribute *attr = &pg->vertex_attributes[i];
|
||||
@@ -552,6 +568,7 @@ void pgraph_gl_flush_draw(NV2AState *d)
|
||||
nv2a_profile_inc_counter(NV2A_PROF_INLINE_ARRAYS);
|
||||
|
||||
unsigned int index_count = pgraph_gl_bind_inline_array(d);
|
||||
pgraph_gl_bind_shaders(pg);
|
||||
|
||||
PrimRewrite prim_rw = pgraph_prim_rewrite_sequential(
|
||||
&r->prim_rewrite_buf, assembly, 0, index_count);
|
||||
|
||||
@@ -126,7 +126,12 @@ static void pgraph_gl_init(NV2AState *d, Error **errp)
|
||||
assert(glo_check_extension("GL_ARB_ES2_compatibility"));
|
||||
#endif
|
||||
|
||||
#ifndef __ANDROID__ /* GL_SMOOTH_LINE_WIDTH_RANGE not valid in GLES 3.x */
|
||||
glGetFloatv(GL_SMOOTH_LINE_WIDTH_RANGE, r->supported_smooth_line_width_range);
|
||||
#else
|
||||
r->supported_smooth_line_width_range[0] = 1.0f;
|
||||
r->supported_smooth_line_width_range[1] = 1.0f;
|
||||
#endif
|
||||
glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, r->supported_aliased_line_width_range);
|
||||
|
||||
pgraph_gl_init_surfaces(pg);
|
||||
@@ -143,6 +148,14 @@ static void pgraph_gl_init(NV2AState *d, Error **errp)
|
||||
|
||||
r->supported_extensions.texture_filter_anisotropic =
|
||||
glo_check_extension("GL_EXT_texture_filter_anisotropic");
|
||||
r->supported_extensions.max_texture_max_anisotropy = 1.0f;
|
||||
if (r->supported_extensions.texture_filter_anisotropic) {
|
||||
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT,
|
||||
&r->supported_extensions.max_texture_max_anisotropy);
|
||||
if (r->supported_extensions.max_texture_max_anisotropy < 1.0f) {
|
||||
r->supported_extensions.max_texture_max_anisotropy = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pgraph_gl_finalize(NV2AState *d)
|
||||
|
||||
@@ -242,6 +242,7 @@ typedef struct PGRAPHGLState {
|
||||
|
||||
struct supported_extensions {
|
||||
GLboolean texture_filter_anisotropic;
|
||||
GLfloat max_texture_max_anisotropy;
|
||||
} supported_extensions;
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
@@ -29,6 +29,10 @@
|
||||
#include "debug.h"
|
||||
#include "renderer.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <android/log.h>
|
||||
#endif
|
||||
|
||||
static GLenum get_gl_primitive_mode(enum ShaderPrimitiveMode primitive_mode)
|
||||
{
|
||||
switch (primitive_mode) {
|
||||
@@ -41,6 +45,31 @@ static GLenum get_gl_primitive_mode(enum ShaderPrimitiveMode primitive_mode)
|
||||
}
|
||||
}
|
||||
|
||||
static void log_shader_source_with_line_numbers(const char *name,
|
||||
const char *code)
|
||||
{
|
||||
int line_no = 1;
|
||||
const char *line = code;
|
||||
|
||||
while (line && *line) {
|
||||
const char *line_end = strchr(line, '\n');
|
||||
size_t len = line_end ? (size_t)(line_end - line) : strlen(line);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
__android_log_print(ANDROID_LOG_ERROR, "xemu-android",
|
||||
"%s %4d | %.*s", name, line_no, (int)len, line);
|
||||
#endif
|
||||
fprintf(stderr, "%s %4d | %.*s\n", name, line_no, (int)len, line);
|
||||
|
||||
if (!line_end) {
|
||||
break;
|
||||
}
|
||||
|
||||
line = line_end + 1;
|
||||
line_no++;
|
||||
}
|
||||
}
|
||||
|
||||
static GLuint create_gl_shader(GLenum gl_shader_type,
|
||||
const char *code,
|
||||
const char *name)
|
||||
@@ -64,7 +93,12 @@ static GLuint create_gl_shader(GLenum gl_shader_type,
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
|
||||
log = g_malloc(log_length * sizeof(GLchar));
|
||||
glGetShaderInfoLog(shader, log_length, NULL, log);
|
||||
fprintf(stderr, "%s\n\n" "nv2a: %s compilation failed: %s\n", code, name, log);
|
||||
#ifdef __ANDROID__
|
||||
__android_log_print(ANDROID_LOG_ERROR, "xemu-android",
|
||||
"nv2a: %s compilation failed: %s", name, log);
|
||||
#endif
|
||||
fprintf(stderr, "nv2a: %s compilation failed: %s\n", name, log);
|
||||
log_shader_source_with_line_numbers(name, code);
|
||||
g_free(log);
|
||||
|
||||
NV2A_GL_DGROUP_END();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -80,6 +80,8 @@ void pgraph_gl_bind_vertex_attributes(NV2AState *d, unsigned int min_element,
|
||||
}
|
||||
|
||||
pg->compressed_attrs = 0;
|
||||
pg->uniform_attrs = 0;
|
||||
pg->swizzle_attrs = 0;
|
||||
|
||||
for (int i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) {
|
||||
VertexAttribute *attr = &pg->vertex_attributes[i];
|
||||
@@ -97,13 +99,21 @@ void pgraph_gl_bind_vertex_attributes(NV2AState *d, unsigned int min_element,
|
||||
GLenum gl_type;
|
||||
GLboolean gl_normalize;
|
||||
bool needs_conversion = false;
|
||||
bool d3d_swizzle = false;
|
||||
|
||||
switch (attr->format) {
|
||||
case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_UB_D3D:
|
||||
gl_type = GL_UNSIGNED_BYTE;
|
||||
gl_normalize = GL_TRUE;
|
||||
#ifdef __ANDROID__
|
||||
gl_count = attr->count;
|
||||
if (attr->count == 4) {
|
||||
d3d_swizzle = true;
|
||||
}
|
||||
#else
|
||||
// http://www.opengl.org/registry/specs/ARB/vertex_array_bgra.txt
|
||||
gl_count = GL_BGRA;
|
||||
#endif
|
||||
break;
|
||||
case NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE_UB_OGL:
|
||||
gl_type = GL_UNSIGNED_BYTE;
|
||||
@@ -188,6 +198,9 @@ void pgraph_gl_bind_vertex_attributes(NV2AState *d, unsigned int min_element,
|
||||
}
|
||||
|
||||
glEnableVertexAttribArray(i);
|
||||
if (d3d_swizzle) {
|
||||
pg->swizzle_attrs |= (1 << i);
|
||||
}
|
||||
last_entry += stride * provoking_element_index;
|
||||
pgraph_update_inline_value(attr, last_entry);
|
||||
}
|
||||
|
||||
@@ -179,19 +179,10 @@ void pgraph_glsl_set_psh_state(PGRAPHState *pg, PshState *state)
|
||||
}
|
||||
}
|
||||
|
||||
/* Keep track of whether texture data has been loaded as signed
|
||||
* normalized integers or not. This dictates whether or not we will need
|
||||
* to re-map in fragment shader for certain texture modes (e.g.
|
||||
* bumpenvmap).
|
||||
*
|
||||
* FIXME: When signed texture data is loaded as unsigned and remapped in
|
||||
* fragment shader, there may be interpolation artifacts. Fix this to
|
||||
* support signed textures more appropriately.
|
||||
*/
|
||||
#if 0 // FIXME
|
||||
psh->snorm_tex[i] = (f.gl_internal_format == GL_RGB8_SNORM)
|
||||
|| (f.gl_internal_format == GL_RG8_SNORM);
|
||||
#endif
|
||||
/* Keep track of textures that are uploaded as signed normalized data.
|
||||
* Those must not be remapped a second time in the fragment shader. */
|
||||
state->snorm_tex[i] =
|
||||
color_format == NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R6G5B5;
|
||||
state->shadow_map[i] = f.depth;
|
||||
|
||||
uint32_t filter = pgraph_reg_r(pg, NV_PGRAPH_TEXFILTER0 + i * 4);
|
||||
@@ -767,7 +758,7 @@ static void apply_border_adjustment(const struct PixelShader *ps, MString *vars,
|
||||
mstring_append_fmt(
|
||||
vars,
|
||||
"vec3 t%dLogicalSize = vec3(%f, %f, %f);\n"
|
||||
"%s.xyz = (%s.xyz * t%dLogicalSize + vec3(4, 4, 4)) * vec3(%f, %f, %f);\n",
|
||||
"%s.xyz = (%s.xyz * t%dLogicalSize + vec3(4.0, 4.0, 4.0)) * vec3(%f, %f, %f);\n",
|
||||
i, ps->state->border_logical_size[i][0], ps->state->border_logical_size[i][1], ps->state->border_logical_size[i][2],
|
||||
var_name, var_name, i, ps->state->border_inv_real_size[i][0], ps->state->border_inv_real_size[i][1], ps->state->border_inv_real_size[i][2]);
|
||||
}
|
||||
@@ -783,7 +774,7 @@ static void apply_convolution_filter(const struct PixelShader *ps, MString *vars
|
||||
mstring_append_fmt(vars,
|
||||
"vec4 t%d = vec4(0.0);\n"
|
||||
"for (int i = 0; i < 9; i++) {\n"
|
||||
" vec3 texCoordDelta = vec3(convolution3x3[i], 0);\n"
|
||||
" vec3 texCoordDelta = vec3(convolution3x3[i], 0.0);\n"
|
||||
" texCoordDelta.xy /= vec2(textureSize(texSamp%d, 0));\n"
|
||||
" t%d += textureProj(texSamp%d, %s(pT%d.xyw) + texCoordDelta) * gaussian3x3[i];\n"
|
||||
"}\n", tex, tex, tex, tex, tex_remap, tex);
|
||||
@@ -855,22 +846,23 @@ static MString* psh_convert(struct PixelShader *ps)
|
||||
|
||||
mstring_append_fmt(preflight,
|
||||
"float sign1(float x) {\n"
|
||||
" x *= 255.0;\n"
|
||||
" return (x-128.0)/127.0;\n"
|
||||
" float xf = float(x) * 255.0;\n"
|
||||
" return (xf - 128.0) / 127.0;\n"
|
||||
"}\n"
|
||||
"float sign2(float x) {\n"
|
||||
" x *= 255.0;\n"
|
||||
" if (x >= 128.0) return (x-255.5)/127.5;\n"
|
||||
" else return (x+0.5)/127.5;\n"
|
||||
" float xf = float(x) * 255.0;\n"
|
||||
" if (xf >= 128.0) return (xf - 255.5) / 127.5;\n"
|
||||
" else return (xf + 0.5) / 127.5;\n"
|
||||
"}\n"
|
||||
"float sign3(float x) {\n"
|
||||
" x *= 255.0;\n"
|
||||
" if (x >= 128.0) return (x-256.0)/127.0;\n"
|
||||
" else return (x)/127.0;\n"
|
||||
" float xf = float(x) * 255.0;\n"
|
||||
" if (xf >= 128.0) return (xf - 256.0) / 127.0;\n"
|
||||
" else return xf / 127.0;\n"
|
||||
"}\n"
|
||||
"float sign3_to_0_to_1(float x) {\n"
|
||||
" if (x >= 0.0) return x/2.0;\n"
|
||||
" else return 1.0+x/2.0;\n"
|
||||
" float xf = float(x);\n"
|
||||
" if (xf >= 0.0) return xf / 2.0;\n"
|
||||
" else return 1.0 + xf / 2.0;\n"
|
||||
"}\n"
|
||||
"vec3 dotmap_zero_to_one(vec4 col) {\n"
|
||||
" return col.rgb;\n"
|
||||
|
||||
@@ -262,6 +262,8 @@ GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz
|
||||
static char alpha_source_specular[] = "specular.a";
|
||||
static char alpha_source_material[] = "material_alpha";
|
||||
const char *alpha_source = alpha_source_diffuse;
|
||||
const char *ambient_source_rgb = "diffuse.rgb";
|
||||
const char *emission_source_rgb = "diffuse.rgb";
|
||||
if (state->fixed_function.diffuse_src == MATERIAL_COLOR_SRC_MATERIAL) {
|
||||
alpha_source = alpha_source_material;
|
||||
} else if (state->fixed_function.diffuse_src == MATERIAL_COLOR_SRC_SPECULAR) {
|
||||
@@ -269,21 +271,23 @@ GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz
|
||||
}
|
||||
|
||||
if (state->fixed_function.ambient_src == MATERIAL_COLOR_SRC_MATERIAL) {
|
||||
mstring_append_fmt(body, "oD0 = vec4(sceneAmbientColor, %s);\n", alpha_source);
|
||||
ambient_source_rgb = "materialEmissionColor.rgb";
|
||||
} else if (state->fixed_function.ambient_src == MATERIAL_COLOR_SRC_DIFFUSE) {
|
||||
mstring_append_fmt(body, "oD0 = vec4(diffuse.rgb, %s);\n", alpha_source);
|
||||
ambient_source_rgb = "diffuse.rgb";
|
||||
} else if (state->fixed_function.ambient_src == MATERIAL_COLOR_SRC_SPECULAR) {
|
||||
mstring_append_fmt(body, "oD0 = vec4(specular.rgb, %s);\n", alpha_source);
|
||||
ambient_source_rgb = "specular.rgb";
|
||||
}
|
||||
|
||||
mstring_append(body, "oD0.rgb *= materialEmissionColor.rgb;\n");
|
||||
if (state->fixed_function.emission_src == MATERIAL_COLOR_SRC_MATERIAL) {
|
||||
mstring_append(body, "oD0.rgb += sceneAmbientColor;\n");
|
||||
emission_source_rgb = "materialEmissionColor.rgb";
|
||||
} else if (state->fixed_function.emission_src == MATERIAL_COLOR_SRC_DIFFUSE) {
|
||||
mstring_append(body, "oD0.rgb += diffuse.rgb;\n");
|
||||
emission_source_rgb = "diffuse.rgb";
|
||||
} else if (state->fixed_function.emission_src == MATERIAL_COLOR_SRC_SPECULAR) {
|
||||
mstring_append(body, "oD0.rgb += specular.rgb;\n");
|
||||
emission_source_rgb = "specular.rgb";
|
||||
}
|
||||
mstring_append_fmt(body, "oD0 = vec4(sceneAmbientColor * %s, %s);\n",
|
||||
ambient_source_rgb, alpha_source);
|
||||
mstring_append_fmt(body, "oD0.rgb += %s;\n", emission_source_rgb);
|
||||
|
||||
mstring_append(body, "oD1 = vec4(0.0, 0.0, 0.0, specular.a);\n");
|
||||
|
||||
@@ -349,7 +353,7 @@ GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz
|
||||
/* https://docs.microsoft.com/en-us/windows/win32/direct3d9/attenuation-and-spotlight-factor#spotlight-factor */
|
||||
mstring_append_fmt(body,
|
||||
" vec4 spotDir = lightSpotDirection(%d);\n"
|
||||
" float invScale = 1/length(spotDir.xyz);\n"
|
||||
" float invScale = 1.0 / length(spotDir.xyz);\n"
|
||||
" float cosHalfPhi = -invScale*spotDir.w;\n"
|
||||
" float cosHalfTheta = invScale + cosHalfPhi;\n"
|
||||
" float spotDirDotVP = dot(spotDir.xyz, VP);\n"
|
||||
@@ -485,7 +489,7 @@ GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz
|
||||
" oPos.xy = roundScreenCoords(oPos.xy);\n"
|
||||
" vec4 vtxPos = vec4(oPos.xy, oPos.z / oPos.w, oPos.w);\n"
|
||||
" oPos.z = oPos.z / clipRange.y;\n"
|
||||
" oPos.xy = (2.0f * oPos.xy - surfaceSize) / surfaceSize;\n"
|
||||
" oPos.xy = (2.0 * oPos.xy - surfaceSize) / surfaceSize;\n"
|
||||
" oPos.xy *= oPos.w;\n"
|
||||
);
|
||||
|
||||
@@ -495,7 +499,7 @@ GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz
|
||||
" float d_e = length(position * modelViewMat0);\n"
|
||||
" float ptMinSize = min(pointParams[7], 63.875);\n"
|
||||
" float ptMaxSize = min(pointParams[3] + ptMinSize, 63.875);\n"
|
||||
" oPts.x = 1/sqrt(pointParams[0] + pointParams[1] * d_e + pointParams[2] * d_e * d_e) + pointParams[6];\n");
|
||||
" oPts.x = 1.0 / sqrt(pointParams[0] + pointParams[1] * d_e + pointParams[2] * d_e * d_e) + pointParams[6];\n");
|
||||
mstring_append_fmt(body,
|
||||
" oPts.x = clamp(oPts.x * pointParams[3] + pointParams[7], ptMinSize, ptMaxSize) * float(%d);\n",
|
||||
state->surface_scale_factor);
|
||||
|
||||
@@ -696,7 +696,7 @@ static const char* vsh_header =
|
||||
"vec4 _LOG(float src)\n"
|
||||
"{\n"
|
||||
" float tmp = abs(src);\n"
|
||||
" if (tmp == 0.0) { return vec4(-INFINITY, 1.0f, -INFINITY, 1.0f); }\n"
|
||||
" if (tmp == 0.0) { return vec4(-INFINITY, 1.0, -INFINITY, 1.0); }\n"
|
||||
" vec4 result;\n"
|
||||
" result.x = floor(log2(tmp));\n"
|
||||
" result.y = tmp / exp2(floor(log2(tmp)));\n"
|
||||
@@ -757,7 +757,7 @@ void pgraph_glsl_gen_vsh_prog(uint16_t version, const uint32_t *tokens,
|
||||
" oPos.xy = roundScreenCoords(oPos.xy);\n"
|
||||
" oPos.w = clampAwayZeroInf(oPos.w);\n"
|
||||
" vec4 vtxPos = oPos;\n"
|
||||
" oPos.xy = (2.0f * oPos.xy - surfaceSize) / surfaceSize;\n"
|
||||
" oPos.xy = (2.0 * oPos.xy - surfaceSize) / surfaceSize;\n"
|
||||
" oPos.z = oPos.z / clipRange.y;\n"
|
||||
|
||||
/* Undo perspective divide by w.
|
||||
|
||||
@@ -229,7 +229,7 @@ MString *pgraph_glsl_gen_vsh(const VshState *state, GenVshGlslOptions opts)
|
||||
// fractional part and to convert floating-point coordinates by
|
||||
// by truncating (not flooring).
|
||||
"vec2 roundScreenCoords(vec2 pos) {\n"
|
||||
" return trunc(pos * 16.0f) / 16.0f;\n"
|
||||
" return trunc(pos * 16.0) / 16.0;\n"
|
||||
"}\n");
|
||||
|
||||
pgraph_glsl_get_vtx_header(header, opts.vulkan, state->smooth_shading,
|
||||
|
||||
@@ -2487,10 +2487,8 @@ DEF_METHOD_INC(NV097, SET_TEXCOORD3_2F)
|
||||
do { \
|
||||
VertexAttribute *attribute = &pg->vertex_attributes[(attr_index)]; \
|
||||
pgraph_allocate_inline_buffer_vertices(pg, (attr_index)); \
|
||||
attribute->inline_value[0] = (parameter & 0xFF) / 255.0f; \
|
||||
attribute->inline_value[1] = ((parameter >> 8) & 0xFF) / 255.0f; \
|
||||
attribute->inline_value[2] = ((parameter >> 16) & 0xFF) / 255.0f; \
|
||||
attribute->inline_value[3] = ((parameter >> 24) & 0xFF) / 255.0f; \
|
||||
pgraph_argb_pack32_to_rgba_float(parameter, \
|
||||
attribute->inline_value); \
|
||||
} while (0)
|
||||
|
||||
DEF_METHOD_INC(NV097, SET_DIFFUSE_COLOR4UB)
|
||||
@@ -2917,10 +2915,14 @@ DEF_METHOD_INC(NV097, SET_VERTEX_DATA4UB)
|
||||
int slot = (method - NV097_SET_VERTEX_DATA4UB) / 4;
|
||||
VertexAttribute *attribute = &pg->vertex_attributes[slot];
|
||||
pgraph_allocate_inline_buffer_vertices(pg, slot);
|
||||
attribute->inline_value[0] = (parameter & 0xFF) / 255.0;
|
||||
attribute->inline_value[1] = ((parameter >> 8) & 0xFF) / 255.0;
|
||||
attribute->inline_value[2] = ((parameter >> 16) & 0xFF) / 255.0;
|
||||
attribute->inline_value[3] = ((parameter >> 24) & 0xFF) / 255.0;
|
||||
if (slot == NV2A_VERTEX_ATTR_DIFFUSE || slot == NV2A_VERTEX_ATTR_SPECULAR) {
|
||||
pgraph_argb_pack32_to_rgba_float(parameter, attribute->inline_value);
|
||||
} else {
|
||||
attribute->inline_value[0] = (parameter & 0xFF) / 255.0;
|
||||
attribute->inline_value[1] = ((parameter >> 8) & 0xFF) / 255.0;
|
||||
attribute->inline_value[2] = ((parameter >> 16) & 0xFF) / 255.0;
|
||||
attribute->inline_value[3] = ((parameter >> 24) & 0xFF) / 255.0;
|
||||
}
|
||||
if (slot == 0) {
|
||||
pgraph_finish_inline_buffer_vertex(pg);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user