2013-10-04 12:13:59 -07:00
|
|
|
/******************************************************************************
|
|
|
|
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
2014-01-11 15:34:15 -08:00
|
|
|
Copyright (C) 2014 by Zachary Lund <admin@computerquip.com>
|
2013-10-04 12:13:59 -07:00
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
2013-12-02 21:24:38 -08:00
|
|
|
the Free Software Foundation, either version 2 of the License, or
|
2013-10-04 12:13:59 -07:00
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
******************************************************************************/
|
|
|
|
|
2013-11-26 22:07:27 -08:00
|
|
|
#include <graphics/matrix3.h>
|
2013-10-02 23:14:15 -07:00
|
|
|
#include "gl-subsystem.h"
|
|
|
|
|
2014-01-25 17:35:09 -08:00
|
|
|
/* Goofy Windows.h macros need to be removed */
|
|
|
|
#undef far
|
|
|
|
#undef near
|
|
|
|
|
2014-12-09 13:49:25 -08:00
|
|
|
/* #define SHOW_ALL_GL_MESSAGES */
|
|
|
|
|
2014-01-05 17:55:19 -08:00
|
|
|
#ifdef _DEBUG
|
2014-01-03 21:14:35 -08:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
static void APIENTRY gl_debug_proc(GLenum source, GLenum type, GLuint id,
|
|
|
|
GLenum severity, GLsizei length,
|
|
|
|
const GLchar *message, const GLvoid *data)
|
2014-01-03 21:14:35 -08:00
|
|
|
{
|
2014-04-09 11:04:58 -07:00
|
|
|
UNUSED_PARAMETER(id);
|
|
|
|
UNUSED_PARAMETER(data);
|
|
|
|
|
2014-07-10 23:36:42 -07:00
|
|
|
char *source_str, *type_str, *severity_str;
|
|
|
|
|
2014-12-09 13:49:25 -08:00
|
|
|
/* frames can get a bit too much spam with irrelevant/insignificant opengl
|
|
|
|
* debug messages */
|
|
|
|
#ifndef SHOW_ALL_GL_MESSAGES
|
|
|
|
if (type > GL_DEBUG_TYPE_PORTABILITY &&
|
|
|
|
severity != GL_DEBUG_SEVERITY_HIGH) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
switch (source) {
|
2014-07-10 23:36:42 -07:00
|
|
|
case GL_DEBUG_SOURCE_API:
|
2019-06-22 22:13:45 -07:00
|
|
|
source_str = "API";
|
|
|
|
break;
|
2014-07-10 23:36:42 -07:00
|
|
|
case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
|
2019-06-22 22:13:45 -07:00
|
|
|
source_str = "Window System";
|
|
|
|
break;
|
2014-07-10 23:36:42 -07:00
|
|
|
case GL_DEBUG_SOURCE_SHADER_COMPILER:
|
2019-06-22 22:13:45 -07:00
|
|
|
source_str = "Shader Compiler";
|
|
|
|
break;
|
2014-07-10 23:36:42 -07:00
|
|
|
case GL_DEBUG_SOURCE_THIRD_PARTY:
|
2019-06-22 22:13:45 -07:00
|
|
|
source_str = "Third Party";
|
|
|
|
break;
|
2014-07-10 23:36:42 -07:00
|
|
|
case GL_DEBUG_SOURCE_APPLICATION:
|
2019-06-22 22:13:45 -07:00
|
|
|
source_str = "Application";
|
|
|
|
break;
|
2014-07-10 23:36:42 -07:00
|
|
|
case GL_DEBUG_SOURCE_OTHER:
|
2019-06-22 22:13:45 -07:00
|
|
|
source_str = "Other";
|
|
|
|
break;
|
2014-07-10 23:36:42 -07:00
|
|
|
default:
|
|
|
|
source_str = "Unknown";
|
2014-07-06 14:02:42 -07:00
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
switch (type) {
|
2014-07-10 23:36:42 -07:00
|
|
|
case GL_DEBUG_TYPE_ERROR:
|
2019-06-22 22:13:45 -07:00
|
|
|
type_str = "Error";
|
|
|
|
break;
|
2014-07-10 23:36:42 -07:00
|
|
|
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
|
2019-06-22 22:13:45 -07:00
|
|
|
type_str = "Deprecated Behavior";
|
|
|
|
break;
|
2014-07-10 23:36:42 -07:00
|
|
|
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
|
2019-06-22 22:13:45 -07:00
|
|
|
type_str = "Undefined Behavior";
|
|
|
|
break;
|
2014-07-10 23:36:42 -07:00
|
|
|
case GL_DEBUG_TYPE_PORTABILITY:
|
2019-06-22 22:13:45 -07:00
|
|
|
type_str = "Portability";
|
|
|
|
break;
|
2014-07-10 23:36:42 -07:00
|
|
|
case GL_DEBUG_TYPE_PERFORMANCE:
|
2019-06-22 22:13:45 -07:00
|
|
|
type_str = "Performance";
|
|
|
|
break;
|
2014-07-10 23:36:42 -07:00
|
|
|
case GL_DEBUG_TYPE_OTHER:
|
2019-06-22 22:13:45 -07:00
|
|
|
type_str = "Other";
|
|
|
|
break;
|
|
|
|
default:
|
2014-07-10 23:36:42 -07:00
|
|
|
type_str = "Unknown";
|
2014-07-06 14:02:42 -07:00
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
switch (severity) {
|
2014-07-10 23:36:42 -07:00
|
|
|
case GL_DEBUG_SEVERITY_HIGH:
|
2019-06-22 22:13:45 -07:00
|
|
|
severity_str = "High";
|
|
|
|
break;
|
2014-07-10 23:36:42 -07:00
|
|
|
case GL_DEBUG_SEVERITY_MEDIUM:
|
2019-06-22 22:13:45 -07:00
|
|
|
severity_str = "Medium";
|
|
|
|
break;
|
2014-07-10 23:36:42 -07:00
|
|
|
case GL_DEBUG_SEVERITY_LOW:
|
2019-06-22 22:13:45 -07:00
|
|
|
severity_str = "Low";
|
|
|
|
break;
|
2014-07-10 23:36:42 -07:00
|
|
|
case GL_DEBUG_SEVERITY_NOTIFICATION:
|
2019-06-22 22:13:45 -07:00
|
|
|
severity_str = "Notification";
|
|
|
|
break;
|
2014-07-10 23:36:42 -07:00
|
|
|
default:
|
|
|
|
severity_str = "Unknown";
|
2014-07-06 14:02:42 -07:00
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
blog(LOG_DEBUG, "[%s][%s]{%s}: %.*s", source_str, type_str,
|
|
|
|
severity_str, length, message);
|
2014-01-03 21:14:35 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void gl_enable_debug()
|
|
|
|
{
|
2014-04-14 14:28:07 -07:00
|
|
|
if (GLAD_GL_VERSION_4_3) {
|
2014-01-03 21:14:35 -08:00
|
|
|
glDebugMessageCallback(gl_debug_proc, NULL);
|
2014-02-05 23:57:14 -08:00
|
|
|
gl_enable(GL_DEBUG_OUTPUT);
|
2014-04-14 14:28:07 -07:00
|
|
|
} else if (GLAD_GL_ARB_debug_output) {
|
2014-01-07 13:49:55 -08:00
|
|
|
glDebugMessageCallbackARB(gl_debug_proc, NULL);
|
2014-02-05 23:57:14 -08:00
|
|
|
} else {
|
2014-01-06 16:29:59 -08:00
|
|
|
blog(LOG_DEBUG, "Failed to set GL debug callback as it is "
|
2019-06-22 22:13:45 -07:00
|
|
|
"not supported.");
|
2014-01-03 21:14:35 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static void gl_enable_debug() {}
|
|
|
|
#endif
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
static bool gl_init_extensions(struct gs_device *device)
|
2014-01-05 19:05:17 -08:00
|
|
|
{
|
2019-09-12 21:28:26 -07:00
|
|
|
if (!GLAD_GL_VERSION_3_3) {
|
|
|
|
blog(LOG_ERROR,
|
|
|
|
"obs-studio requires OpenGL version 3.3 or higher.");
|
2014-01-05 19:05:17 -08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
gl_enable_debug();
|
|
|
|
|
2019-09-12 21:28:26 -07:00
|
|
|
gl_enable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
|
2014-01-05 19:05:17 -08:00
|
|
|
|
2014-04-14 14:28:07 -07:00
|
|
|
if (GLAD_GL_VERSION_4_3 || GLAD_GL_ARB_copy_image)
|
2014-01-05 19:05:17 -08:00
|
|
|
device->copy_type = COPY_TYPE_ARB;
|
2014-04-14 14:28:07 -07:00
|
|
|
else if (GLAD_GL_NV_copy_image)
|
2014-01-05 19:05:17 -08:00
|
|
|
device->copy_type = COPY_TYPE_NV;
|
|
|
|
else
|
2014-02-09 10:37:22 -08:00
|
|
|
device->copy_type = COPY_TYPE_FBO_BLIT;
|
2014-01-05 19:05:17 -08:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-10-12 20:18:05 -07:00
|
|
|
static void clear_textures(struct gs_device *device)
|
|
|
|
{
|
|
|
|
GLenum i;
|
|
|
|
for (i = 0; i < GS_MAX_TEXTURES; i++) {
|
|
|
|
if (device->cur_textures[i]) {
|
|
|
|
gl_active_texture(GL_TEXTURE0 + i);
|
|
|
|
gl_bind_texture(device->cur_textures[i]->gl_target, 0);
|
|
|
|
device->cur_textures[i] = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-10 23:03:42 -07:00
|
|
|
void convert_sampler_info(struct gs_sampler_state *sampler,
|
2019-06-22 22:13:45 -07:00
|
|
|
const struct gs_sampler_info *info)
|
2013-10-09 15:48:16 -07:00
|
|
|
{
|
2013-11-02 14:44:40 -07:00
|
|
|
GLint max_anisotropy_max;
|
2013-10-09 15:48:16 -07:00
|
|
|
convert_filter(info->filter, &sampler->min_filter,
|
2019-06-22 22:13:45 -07:00
|
|
|
&sampler->mag_filter);
|
|
|
|
sampler->address_u = convert_address_mode(info->address_u);
|
|
|
|
sampler->address_v = convert_address_mode(info->address_v);
|
|
|
|
sampler->address_w = convert_address_mode(info->address_w);
|
2013-10-09 15:48:16 -07:00
|
|
|
sampler->max_anisotropy = info->max_anisotropy;
|
2013-10-31 10:06:01 -07:00
|
|
|
|
2013-11-02 14:44:40 -07:00
|
|
|
max_anisotropy_max = 1;
|
2019-09-12 21:28:26 -07:00
|
|
|
if (GLAD_GL_EXT_texture_filter_anisotropic) {
|
|
|
|
glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT,
|
|
|
|
&max_anisotropy_max);
|
|
|
|
gl_success("glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT)");
|
|
|
|
}
|
2013-10-31 10:06:01 -07:00
|
|
|
|
2013-11-15 06:10:21 -08:00
|
|
|
if (1 <= sampler->max_anisotropy &&
|
|
|
|
sampler->max_anisotropy <= max_anisotropy_max)
|
2013-10-31 10:06:01 -07:00
|
|
|
return;
|
|
|
|
|
2013-11-15 06:10:21 -08:00
|
|
|
if (sampler->max_anisotropy < 1)
|
|
|
|
sampler->max_anisotropy = 1;
|
2013-10-31 10:06:01 -07:00
|
|
|
else if (sampler->max_anisotropy > max_anisotropy_max)
|
|
|
|
sampler->max_anisotropy = max_anisotropy_max;
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
blog(LOG_DEBUG,
|
|
|
|
"convert_sampler_info: 1 <= max_anisotropy <= "
|
|
|
|
"%d violated, selected: %d, set: %d",
|
|
|
|
max_anisotropy_max, info->max_anisotropy, sampler->max_anisotropy);
|
2013-10-09 15:48:16 -07:00
|
|
|
}
|
|
|
|
|
2014-08-07 23:42:07 -07:00
|
|
|
const char *device_get_name(void)
|
2014-07-20 15:31:45 -07:00
|
|
|
{
|
|
|
|
return "OpenGL";
|
|
|
|
}
|
|
|
|
|
2014-08-07 23:42:07 -07:00
|
|
|
int device_get_type(void)
|
2014-07-20 16:19:43 -07:00
|
|
|
{
|
|
|
|
return GS_DEVICE_OPENGL;
|
|
|
|
}
|
|
|
|
|
2014-02-16 18:28:21 -08:00
|
|
|
const char *device_preprocessor_name(void)
|
|
|
|
{
|
|
|
|
return "_OPENGL";
|
|
|
|
}
|
|
|
|
|
2015-08-01 18:45:13 -07:00
|
|
|
int device_create(gs_device_t **p_device, uint32_t adapter)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2014-02-09 11:34:07 -08:00
|
|
|
struct gs_device *device = bzalloc(sizeof(struct gs_device));
|
2014-07-20 17:40:57 -07:00
|
|
|
int errorcode = GS_ERROR_FAIL;
|
2013-10-02 23:14:15 -07:00
|
|
|
|
2017-04-06 23:47:15 -07:00
|
|
|
blog(LOG_INFO, "---------------------------------");
|
|
|
|
blog(LOG_INFO, "Initializing OpenGL...");
|
|
|
|
|
2015-08-01 18:45:13 -07:00
|
|
|
device->plat = gl_platform_create(device, adapter);
|
2013-10-12 12:35:38 -07:00
|
|
|
if (!device->plat)
|
|
|
|
goto fail;
|
|
|
|
|
2018-02-11 15:12:58 -08:00
|
|
|
const char *glVendor = (const char *)glGetString(GL_VENDOR);
|
|
|
|
const char *glRenderer = (const char *)glGetString(GL_RENDERER);
|
|
|
|
|
|
|
|
blog(LOG_INFO, "Loading up OpenGL on adapter %s %s", glVendor,
|
2019-06-22 22:13:45 -07:00
|
|
|
glRenderer);
|
2018-02-11 15:12:58 -08:00
|
|
|
|
2014-07-20 17:40:57 -07:00
|
|
|
if (!gl_init_extensions(device)) {
|
|
|
|
errorcode = GS_ERROR_NOT_SUPPORTED;
|
2014-01-05 19:05:17 -08:00
|
|
|
goto fail;
|
2014-07-20 17:40:57 -07:00
|
|
|
}
|
2017-04-06 23:47:15 -07:00
|
|
|
|
2018-02-11 15:12:58 -08:00
|
|
|
const char *glVersion = (const char *)glGetString(GL_VERSION);
|
2019-06-22 22:13:45 -07:00
|
|
|
const char *glShadingLanguage =
|
|
|
|
(const char *)glGetString(GL_SHADING_LANGUAGE_VERSION);
|
2018-02-11 15:12:58 -08:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
blog(LOG_INFO,
|
|
|
|
"OpenGL loaded successfully, version %s, shading "
|
|
|
|
"language %s",
|
|
|
|
glVersion, glShadingLanguage);
|
2018-02-11 15:12:58 -08:00
|
|
|
|
2014-01-05 19:05:17 -08:00
|
|
|
gl_enable(GL_CULL_FACE);
|
2019-06-24 21:13:04 -07:00
|
|
|
gl_gen_vertex_arrays(1, &device->empty_vao);
|
2019-06-22 22:13:45 -07:00
|
|
|
|
libobs-opengl: OpenGL thread-safety on Mac
Attempt to fix threading issues that cause OBS to force-crash when
compiled with latest Xcode. There are two places where the new SDK
introduces a force-crash because operations are not happening on the
main thread: when we modify the context view to switch swap chains, and
when we resize a swap chain.
Instead of using just one context for all rendering, we create an
additional context for each swap chain, set each view once on
initialization, and switch contexts only in present to blit the final
framebuffer. This is an extra copy, but it's pretty hairy to optimize
away, and it's not worth potential regressions just to speed up Mac.
For resizing, we schedule the update code to run on the main thread from
the render thread. Ideally, we wouldn't have to round trip the logic
from main thread to graphics thread and back, but I don't think we want
to hack up the interface for this, especially since OpenGL will give way
to Metal soon enough.
2019-12-25 22:25:38 -08:00
|
|
|
gl_clear_context(device);
|
2015-08-01 18:45:13 -07:00
|
|
|
device->cur_swap = NULL;
|
2013-10-16 23:31:18 -07:00
|
|
|
|
2016-11-20 01:43:35 -08:00
|
|
|
#ifdef _WIN32
|
|
|
|
blog(LOG_INFO, "Warning: The OpenGL renderer is currently in use. "
|
2019-06-22 22:13:45 -07:00
|
|
|
"On windows, the OpenGL renderer can decrease "
|
|
|
|
"capture performance due to the lack of specific "
|
|
|
|
"features used to maximize capture performance. "
|
|
|
|
"The Direct3D 11 renderer is recommended instead.");
|
2016-11-20 01:43:35 -08:00
|
|
|
#endif
|
|
|
|
|
2014-07-20 17:40:57 -07:00
|
|
|
*p_device = device;
|
|
|
|
return GS_SUCCESS;
|
2013-10-12 12:35:38 -07:00
|
|
|
|
|
|
|
fail:
|
|
|
|
blog(LOG_ERROR, "device_create (GL) failed");
|
|
|
|
bfree(device);
|
2014-07-20 17:40:57 -07:00
|
|
|
|
|
|
|
*p_device = NULL;
|
|
|
|
return errorcode;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_destroy(gs_device_t *device)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
|
|
|
if (device) {
|
2014-09-19 04:33:49 -07:00
|
|
|
while (device->first_program)
|
|
|
|
gs_program_destroy(device->first_program);
|
2013-10-12 16:28:10 -07:00
|
|
|
|
2019-06-24 21:13:04 -07:00
|
|
|
gl_delete_vertex_arrays(1, &device->empty_vao);
|
|
|
|
|
2013-10-12 20:18:05 -07:00
|
|
|
da_free(device->proj_stack);
|
2013-10-02 23:14:15 -07:00
|
|
|
gl_platform_destroy(device->plat);
|
|
|
|
bfree(device);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
gs_swapchain_t *device_swapchain_create(gs_device_t *device,
|
2019-06-22 22:13:45 -07:00
|
|
|
const struct gs_init_data *info)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2014-02-09 11:34:07 -08:00
|
|
|
struct gs_swap_chain *swap = bzalloc(sizeof(struct gs_swap_chain));
|
2013-10-03 06:17:43 -07:00
|
|
|
|
|
|
|
swap->device = device;
|
2019-06-22 22:13:45 -07:00
|
|
|
swap->info = *info;
|
|
|
|
swap->wi = gl_windowinfo_create(info);
|
2013-10-03 06:17:43 -07:00
|
|
|
if (!swap->wi) {
|
2014-08-07 23:42:07 -07:00
|
|
|
blog(LOG_ERROR, "device_swapchain_create (GL) failed");
|
|
|
|
gs_swapchain_destroy(swap);
|
2013-10-03 06:17:43 -07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-04-12 11:21:47 -07:00
|
|
|
if (!gl_platform_init_swapchain(swap)) {
|
2014-04-12 04:33:47 -07:00
|
|
|
blog(LOG_ERROR, "gl_platform_init_swapchain failed");
|
2014-08-07 23:42:07 -07:00
|
|
|
gs_swapchain_destroy(swap);
|
2014-04-12 04:33:47 -07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-10-03 06:17:43 -07:00
|
|
|
return swap;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_resize(gs_device_t *device, uint32_t cx, uint32_t cy)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-04 08:55:33 -07:00
|
|
|
/* GL automatically resizes the device, so it doesn't do much */
|
2015-07-30 17:28:40 -07:00
|
|
|
if (device->cur_swap) {
|
|
|
|
device->cur_swap->info.cx = cx;
|
|
|
|
device->cur_swap->info.cy = cy;
|
|
|
|
} else {
|
|
|
|
blog(LOG_WARNING, "device_resize (GL): No active swap");
|
|
|
|
}
|
2013-12-31 02:09:28 -08:00
|
|
|
|
|
|
|
gl_update(device);
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-26 15:25:59 -07:00
|
|
|
void device_get_size(const gs_device_t *device, uint32_t *cx, uint32_t *cy)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2015-07-30 17:28:40 -07:00
|
|
|
if (device->cur_swap) {
|
|
|
|
*cx = device->cur_swap->info.cx;
|
|
|
|
*cy = device->cur_swap->info.cy;
|
|
|
|
} else {
|
|
|
|
blog(LOG_WARNING, "device_get_size (GL): No active swap");
|
|
|
|
*cx = 0;
|
|
|
|
*cy = 0;
|
|
|
|
}
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-26 15:25:59 -07:00
|
|
|
uint32_t device_get_width(const gs_device_t *device)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2015-07-30 17:28:40 -07:00
|
|
|
if (device->cur_swap) {
|
|
|
|
return device->cur_swap->info.cx;
|
|
|
|
} else {
|
|
|
|
blog(LOG_WARNING, "device_get_width (GL): No active swap");
|
|
|
|
return 0;
|
|
|
|
}
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-26 15:25:59 -07:00
|
|
|
uint32_t device_get_height(const gs_device_t *device)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2015-07-30 17:28:40 -07:00
|
|
|
if (device->cur_swap) {
|
|
|
|
return device->cur_swap->info.cy;
|
|
|
|
} else {
|
|
|
|
blog(LOG_WARNING, "device_get_height (GL): No active swap");
|
|
|
|
return 0;
|
|
|
|
}
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
gs_samplerstate_t *
|
|
|
|
device_samplerstate_create(gs_device_t *device,
|
|
|
|
const struct gs_sampler_info *info)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-10 23:03:42 -07:00
|
|
|
struct gs_sampler_state *sampler;
|
2013-10-02 23:14:15 -07:00
|
|
|
|
2014-02-09 11:34:07 -08:00
|
|
|
sampler = bzalloc(sizeof(struct gs_sampler_state));
|
2013-10-12 12:35:38 -07:00
|
|
|
sampler->device = device;
|
2019-06-22 22:13:45 -07:00
|
|
|
sampler->ref = 1;
|
2013-10-10 23:03:42 -07:00
|
|
|
|
|
|
|
convert_sampler_info(sampler, info);
|
|
|
|
return sampler;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2019-07-27 13:31:07 -07:00
|
|
|
gs_timer_t *device_timer_create(gs_device_t *device)
|
|
|
|
{
|
2019-08-17 11:02:03 -07:00
|
|
|
UNUSED_PARAMETER(device);
|
|
|
|
|
2019-07-27 13:31:07 -07:00
|
|
|
struct gs_timer *timer;
|
|
|
|
|
|
|
|
GLuint queries[2];
|
|
|
|
glGenQueries(2, queries);
|
|
|
|
if (!gl_success("glGenQueries"))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
timer = bzalloc(sizeof(struct gs_timer));
|
|
|
|
timer->queries[0] = queries[0];
|
|
|
|
timer->queries[1] = queries[1];
|
|
|
|
|
|
|
|
return timer;
|
|
|
|
}
|
|
|
|
|
|
|
|
gs_timer_range_t *device_timer_range_create(gs_device_t *device)
|
|
|
|
{
|
2019-08-17 11:02:03 -07:00
|
|
|
UNUSED_PARAMETER(device);
|
|
|
|
|
2019-07-27 13:31:07 -07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-09-26 15:25:59 -07:00
|
|
|
enum gs_texture_type device_get_texture_type(const gs_texture_t *texture)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-11 20:14:26 -07:00
|
|
|
return texture->type;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2013-12-21 19:47:01 -08:00
|
|
|
static void strip_mipmap_filter(GLint *filter)
|
|
|
|
{
|
|
|
|
switch (*filter) {
|
2019-06-22 22:13:45 -07:00
|
|
|
case GL_NEAREST:
|
|
|
|
case GL_LINEAR:
|
|
|
|
return;
|
|
|
|
case GL_NEAREST_MIPMAP_NEAREST:
|
|
|
|
case GL_NEAREST_MIPMAP_LINEAR:
|
|
|
|
*filter = GL_NEAREST;
|
|
|
|
return;
|
|
|
|
case GL_LINEAR_MIPMAP_NEAREST:
|
|
|
|
case GL_LINEAR_MIPMAP_LINEAR:
|
|
|
|
*filter = GL_LINEAR;
|
|
|
|
return;
|
2013-12-21 19:47:01 -08:00
|
|
|
}
|
|
|
|
*filter = GL_NEAREST;
|
|
|
|
}
|
|
|
|
|
2014-04-11 13:14:20 -07:00
|
|
|
static inline void apply_swizzle(struct gs_texture *tex)
|
|
|
|
{
|
2014-04-15 12:19:20 -07:00
|
|
|
if (tex->format == GS_A8) {
|
|
|
|
gl_tex_param_i(tex->gl_target, GL_TEXTURE_SWIZZLE_R, GL_ONE);
|
|
|
|
gl_tex_param_i(tex->gl_target, GL_TEXTURE_SWIZZLE_G, GL_ONE);
|
|
|
|
gl_tex_param_i(tex->gl_target, GL_TEXTURE_SWIZZLE_B, GL_ONE);
|
|
|
|
gl_tex_param_i(tex->gl_target, GL_TEXTURE_SWIZZLE_A, GL_RED);
|
|
|
|
}
|
2014-04-11 13:14:20 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool load_texture_sampler(gs_texture_t *tex, gs_samplerstate_t *ss)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
bool success = true;
|
2013-12-21 19:51:11 -08:00
|
|
|
GLint min_filter;
|
|
|
|
|
2013-10-12 12:35:38 -07:00
|
|
|
if (tex->cur_sampler == ss)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (tex->cur_sampler)
|
|
|
|
samplerstate_release(tex->cur_sampler);
|
|
|
|
tex->cur_sampler = ss;
|
|
|
|
if (!ss)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
samplerstate_addref(ss);
|
|
|
|
|
2013-12-21 19:51:11 -08:00
|
|
|
min_filter = ss->min_filter;
|
2014-08-07 23:42:07 -07:00
|
|
|
if (gs_texture_is_rect(tex))
|
2013-12-21 19:47:01 -08:00
|
|
|
strip_mipmap_filter(&min_filter);
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
if (!gl_tex_param_i(tex->gl_target, GL_TEXTURE_MIN_FILTER, min_filter))
|
2013-10-12 12:35:38 -07:00
|
|
|
success = false;
|
|
|
|
if (!gl_tex_param_i(tex->gl_target, GL_TEXTURE_MAG_FILTER,
|
2019-06-22 22:13:45 -07:00
|
|
|
ss->mag_filter))
|
2013-10-12 12:35:38 -07:00
|
|
|
success = false;
|
|
|
|
if (!gl_tex_param_i(tex->gl_target, GL_TEXTURE_WRAP_S, ss->address_u))
|
|
|
|
success = false;
|
|
|
|
if (!gl_tex_param_i(tex->gl_target, GL_TEXTURE_WRAP_T, ss->address_v))
|
|
|
|
success = false;
|
|
|
|
if (!gl_tex_param_i(tex->gl_target, GL_TEXTURE_WRAP_R, ss->address_w))
|
|
|
|
success = false;
|
2019-09-12 21:28:26 -07:00
|
|
|
if (GLAD_GL_EXT_texture_filter_anisotropic) {
|
|
|
|
if (!gl_tex_param_i(tex->gl_target,
|
|
|
|
GL_TEXTURE_MAX_ANISOTROPY_EXT,
|
|
|
|
ss->max_anisotropy))
|
|
|
|
success = false;
|
|
|
|
}
|
2013-10-12 12:35:38 -07:00
|
|
|
|
2014-04-11 13:14:20 -07:00
|
|
|
apply_swizzle(tex);
|
|
|
|
|
2013-10-12 12:35:38 -07:00
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static inline struct gs_shader_param *get_texture_param(gs_device_t *device,
|
2019-06-22 22:13:45 -07:00
|
|
|
int unit)
|
2013-10-12 12:35:38 -07:00
|
|
|
{
|
|
|
|
struct gs_shader *shader = device->cur_pixel_shader;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < shader->params.num; i++) {
|
2019-06-22 22:13:45 -07:00
|
|
|
struct gs_shader_param *param = shader->params.array + i;
|
2014-08-07 23:42:07 -07:00
|
|
|
if (param->type == GS_SHADER_PARAM_TEXTURE) {
|
2013-10-12 12:35:38 -07:00
|
|
|
if (param->texture_id == unit)
|
|
|
|
return param;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_load_texture(gs_device_t *device, gs_texture_t *tex, int unit)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2014-08-07 23:42:07 -07:00
|
|
|
struct gs_shader_param *param;
|
2013-10-12 12:35:38 -07:00
|
|
|
struct gs_sampler_state *sampler;
|
2013-10-12 20:18:05 -07:00
|
|
|
struct gs_texture *cur_tex = device->cur_textures[unit];
|
2013-10-12 12:35:38 -07:00
|
|
|
|
|
|
|
/* need a pixel shader to properly bind textures */
|
|
|
|
if (!device->cur_pixel_shader)
|
2017-02-25 07:45:45 -08:00
|
|
|
goto fail;
|
2013-10-12 12:35:38 -07:00
|
|
|
|
2013-10-12 20:18:05 -07:00
|
|
|
if (cur_tex == tex)
|
2013-10-12 12:35:38 -07:00
|
|
|
return;
|
2013-10-16 23:31:18 -07:00
|
|
|
|
|
|
|
if (!gl_active_texture(GL_TEXTURE0 + unit))
|
|
|
|
goto fail;
|
|
|
|
|
2013-12-13 09:32:05 -08:00
|
|
|
/* the target for the previous text may not be the same as the
|
|
|
|
* next texture, so unbind the previous texture first to be safe */
|
2013-12-13 09:28:05 -08:00
|
|
|
if (cur_tex && (!tex || cur_tex->gl_target != tex->gl_target))
|
2013-10-12 20:18:05 -07:00
|
|
|
gl_bind_texture(cur_tex->gl_target, 0);
|
2013-10-12 12:35:38 -07:00
|
|
|
|
|
|
|
device->cur_textures[unit] = tex;
|
|
|
|
param = get_texture_param(device, unit);
|
|
|
|
if (!param)
|
|
|
|
return;
|
|
|
|
|
|
|
|
param->texture = tex;
|
|
|
|
|
|
|
|
if (!tex)
|
|
|
|
return;
|
|
|
|
|
2015-05-22 22:47:22 -07:00
|
|
|
// texelFetch doesn't need a sampler
|
2016-03-22 12:01:35 -07:00
|
|
|
if (param->sampler_id != (size_t)-1)
|
2015-05-22 22:47:22 -07:00
|
|
|
sampler = device->cur_samplers[param->sampler_id];
|
|
|
|
else
|
|
|
|
sampler = NULL;
|
2013-10-12 12:35:38 -07:00
|
|
|
|
|
|
|
if (!gl_bind_texture(tex->gl_target, tex->texture))
|
|
|
|
goto fail;
|
|
|
|
if (sampler && !load_texture_sampler(tex, sampler))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
blog(LOG_ERROR, "device_load_texture (GL) failed");
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool load_sampler_on_textures(gs_device_t *device, gs_samplerstate_t *ss,
|
2019-06-22 22:13:45 -07:00
|
|
|
int sampler_unit)
|
2013-10-12 12:35:38 -07:00
|
|
|
{
|
|
|
|
struct gs_shader *shader = device->cur_pixel_shader;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < shader->params.num; i++) {
|
2019-06-22 22:13:45 -07:00
|
|
|
struct gs_shader_param *param = shader->params.array + i;
|
2013-10-12 12:35:38 -07:00
|
|
|
|
2014-08-07 23:42:07 -07:00
|
|
|
if (param->type == GS_SHADER_PARAM_TEXTURE &&
|
2013-10-17 17:21:42 -07:00
|
|
|
param->sampler_id == (uint32_t)sampler_unit &&
|
2013-10-12 12:35:38 -07:00
|
|
|
param->texture) {
|
|
|
|
if (!gl_active_texture(GL_TEXTURE0 + param->texture_id))
|
|
|
|
return false;
|
|
|
|
if (!load_texture_sampler(param->texture, ss))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_load_samplerstate(gs_device_t *device, gs_samplerstate_t *ss,
|
2019-06-22 22:13:45 -07:00
|
|
|
int unit)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 12:35:38 -07:00
|
|
|
/* need a pixel shader to properly bind samplers */
|
|
|
|
if (!device->cur_pixel_shader)
|
|
|
|
ss = NULL;
|
|
|
|
|
|
|
|
if (device->cur_samplers[unit] == ss)
|
|
|
|
return;
|
|
|
|
|
|
|
|
device->cur_samplers[unit] = ss;
|
|
|
|
|
|
|
|
if (!ss)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!load_sampler_on_textures(device, ss, unit))
|
|
|
|
blog(LOG_ERROR, "device_load_samplerstate (GL) failed");
|
|
|
|
|
|
|
|
return;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_load_vertexshader(gs_device_t *device, gs_shader_t *vertshader)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 12:35:38 -07:00
|
|
|
if (device->cur_vertex_shader == vertshader)
|
|
|
|
return;
|
|
|
|
|
2014-08-07 23:42:07 -07:00
|
|
|
if (vertshader && vertshader->type != GS_SHADER_VERTEX) {
|
2013-10-12 12:35:38 -07:00
|
|
|
blog(LOG_ERROR, "Specified shader is not a vertex shader");
|
2014-09-19 04:33:49 -07:00
|
|
|
blog(LOG_ERROR, "device_load_vertexshader (GL) failed");
|
|
|
|
return;
|
2013-10-12 12:35:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
device->cur_vertex_shader = vertshader;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2013-10-16 23:31:18 -07:00
|
|
|
static void load_default_pixelshader_samplers(struct gs_device *device,
|
2019-06-22 22:13:45 -07:00
|
|
|
struct gs_shader *ps)
|
2013-10-16 23:31:18 -07:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
if (!ps)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < ps->samplers.num; i++) {
|
|
|
|
struct gs_sampler_state *ss = ps->samplers.array[i];
|
|
|
|
device->cur_samplers[i] = ss;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; i < GS_MAX_TEXTURES; i++)
|
|
|
|
device->cur_samplers[i] = NULL;
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_load_pixelshader(gs_device_t *device, gs_shader_t *pixelshader)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 12:35:38 -07:00
|
|
|
if (device->cur_pixel_shader == pixelshader)
|
|
|
|
return;
|
|
|
|
|
2014-08-07 23:42:07 -07:00
|
|
|
if (pixelshader && pixelshader->type != GS_SHADER_PIXEL) {
|
2013-10-12 12:35:38 -07:00
|
|
|
blog(LOG_ERROR, "Specified shader is not a pixel shader");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
device->cur_pixel_shader = pixelshader;
|
|
|
|
|
2013-10-12 20:18:05 -07:00
|
|
|
clear_textures(device);
|
2013-10-16 23:31:18 -07:00
|
|
|
|
|
|
|
if (pixelshader)
|
|
|
|
load_default_pixelshader_samplers(device, pixelshader);
|
2013-10-12 12:35:38 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
blog(LOG_ERROR, "device_load_pixelshader (GL) failed");
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_load_default_samplerstate(gs_device_t *device, bool b_3d, int unit)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 12:35:38 -07:00
|
|
|
/* TODO */
|
2014-02-14 14:13:36 -08:00
|
|
|
UNUSED_PARAMETER(device);
|
|
|
|
UNUSED_PARAMETER(b_3d);
|
|
|
|
UNUSED_PARAMETER(unit);
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-26 15:25:59 -07:00
|
|
|
gs_shader_t *device_get_vertex_shader(const gs_device_t *device)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 12:35:38 -07:00
|
|
|
return device->cur_vertex_shader;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-26 15:25:59 -07:00
|
|
|
gs_shader_t *device_get_pixel_shader(const gs_device_t *device)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 12:35:38 -07:00
|
|
|
return device->cur_pixel_shader;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-26 15:25:59 -07:00
|
|
|
gs_texture_t *device_get_render_target(const gs_device_t *device)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 12:35:38 -07:00
|
|
|
return device->cur_render_target;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-26 15:25:59 -07:00
|
|
|
gs_zstencil_t *device_get_zstencil_target(const gs_device_t *device)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 12:35:38 -07:00
|
|
|
return device->cur_zstencil_buffer;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool get_tex_dimensions(gs_texture_t *tex, uint32_t *width,
|
2019-06-22 22:13:45 -07:00
|
|
|
uint32_t *height)
|
2013-10-12 16:28:10 -07:00
|
|
|
{
|
|
|
|
if (tex->type == GS_TEXTURE_2D) {
|
2019-06-22 22:13:45 -07:00
|
|
|
struct gs_texture_2d *tex2d = (struct gs_texture_2d *)tex;
|
|
|
|
*width = tex2d->width;
|
2013-10-12 16:28:10 -07:00
|
|
|
*height = tex2d->height;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
} else if (tex->type == GS_TEXTURE_CUBE) {
|
2019-06-22 22:13:45 -07:00
|
|
|
struct gs_texture_cube *cube = (struct gs_texture_cube *)tex;
|
|
|
|
*width = cube->size;
|
2013-10-12 16:28:10 -07:00
|
|
|
*height = cube->size;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
blog(LOG_ERROR, "Texture must be 2D or cubemap");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This automatically manages FBOs so that render targets are always given
|
|
|
|
* an FBO that matches their width/height/format to maximize optimization
|
|
|
|
*/
|
2018-08-31 14:07:00 -07:00
|
|
|
struct fbo_info *get_fbo(gs_texture_t *tex, uint32_t width, uint32_t height)
|
2013-10-12 16:28:10 -07:00
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
if (tex->fbo && tex->fbo->width == width &&
|
|
|
|
tex->fbo->height == height && tex->fbo->format == tex->format)
|
2018-08-31 14:07:00 -07:00
|
|
|
return tex->fbo;
|
2013-10-12 16:28:10 -07:00
|
|
|
|
2018-08-31 14:07:00 -07:00
|
|
|
GLuint fbo;
|
2013-10-12 16:28:10 -07:00
|
|
|
glGenFramebuffers(1, &fbo);
|
|
|
|
if (!gl_success("glGenFramebuffers"))
|
|
|
|
return NULL;
|
|
|
|
|
2018-08-31 14:07:00 -07:00
|
|
|
tex->fbo = bmalloc(sizeof(struct fbo_info));
|
2019-06-22 22:13:45 -07:00
|
|
|
tex->fbo->fbo = fbo;
|
|
|
|
tex->fbo->width = width;
|
|
|
|
tex->fbo->height = height;
|
|
|
|
tex->fbo->format = tex->format;
|
|
|
|
tex->fbo->cur_render_target = NULL;
|
|
|
|
tex->fbo->cur_render_side = 0;
|
2018-08-31 14:07:00 -07:00
|
|
|
tex->fbo->cur_zstencil_buffer = NULL;
|
2013-10-12 16:28:10 -07:00
|
|
|
|
2018-08-31 14:07:00 -07:00
|
|
|
return tex->fbo;
|
2013-10-12 16:28:10 -07:00
|
|
|
}
|
|
|
|
|
2018-08-31 14:07:00 -07:00
|
|
|
static inline struct fbo_info *get_fbo_by_tex(gs_texture_t *tex)
|
2014-02-09 10:37:22 -08:00
|
|
|
{
|
|
|
|
uint32_t width, height;
|
|
|
|
if (!get_tex_dimensions(tex, &width, &height))
|
|
|
|
return NULL;
|
|
|
|
|
2018-08-31 14:07:00 -07:00
|
|
|
return get_fbo(tex, width, height);
|
2014-02-09 10:37:22 -08:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool set_current_fbo(gs_device_t *device, struct fbo_info *fbo)
|
2013-10-12 16:28:10 -07:00
|
|
|
{
|
|
|
|
if (device->cur_fbo != fbo) {
|
|
|
|
GLuint fbo_obj = fbo ? fbo->fbo : 0;
|
|
|
|
if (!gl_bind_framebuffer(GL_DRAW_FRAMEBUFFER, fbo_obj))
|
|
|
|
return false;
|
2015-03-17 18:05:06 -07:00
|
|
|
|
|
|
|
if (device->cur_fbo) {
|
|
|
|
device->cur_fbo->cur_render_target = NULL;
|
|
|
|
device->cur_fbo->cur_zstencil_buffer = NULL;
|
|
|
|
}
|
2013-10-12 16:28:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
device->cur_fbo = fbo;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool attach_rendertarget(struct fbo_info *fbo, gs_texture_t *tex,
|
2019-06-22 22:13:45 -07:00
|
|
|
int side)
|
2013-10-12 16:28:10 -07:00
|
|
|
{
|
|
|
|
if (fbo->cur_render_target == tex)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
fbo->cur_render_target = tex;
|
|
|
|
|
|
|
|
if (tex->type == GS_TEXTURE_2D) {
|
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
|
2019-06-22 22:13:45 -07:00
|
|
|
GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
|
|
|
tex->texture, 0);
|
2013-10-12 16:28:10 -07:00
|
|
|
|
|
|
|
} else if (tex->type == GS_TEXTURE_CUBE) {
|
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
|
2019-06-22 22:13:45 -07:00
|
|
|
GL_COLOR_ATTACHMENT0,
|
|
|
|
GL_TEXTURE_CUBE_MAP_POSITIVE_X + side,
|
|
|
|
tex->texture, 0);
|
2013-10-12 16:28:10 -07:00
|
|
|
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return gl_success("glFramebufferTexture2D");
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool attach_zstencil(struct fbo_info *fbo, gs_zstencil_t *zs)
|
2013-10-12 16:28:10 -07:00
|
|
|
{
|
|
|
|
GLuint zsbuffer = 0;
|
|
|
|
GLenum zs_attachment = GL_DEPTH_STENCIL_ATTACHMENT;
|
|
|
|
|
|
|
|
if (fbo->cur_zstencil_buffer == zs)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
fbo->cur_zstencil_buffer = zs;
|
|
|
|
|
|
|
|
if (zs) {
|
|
|
|
zsbuffer = zs->buffer;
|
|
|
|
zs_attachment = zs->attachment;
|
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, zs_attachment,
|
|
|
|
GL_RENDERBUFFER, zsbuffer);
|
2013-10-12 16:28:10 -07:00
|
|
|
if (!gl_success("glFramebufferRenderbuffer"))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool set_target(gs_device_t *device, gs_texture_t *tex, int side,
|
2019-06-22 22:13:45 -07:00
|
|
|
gs_zstencil_t *zs)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 16:28:10 -07:00
|
|
|
struct fbo_info *fbo;
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
if (device->cur_render_target == tex &&
|
|
|
|
device->cur_zstencil_buffer == zs &&
|
|
|
|
device->cur_render_side == side)
|
2013-10-12 16:28:10 -07:00
|
|
|
return true;
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
device->cur_render_target = tex;
|
|
|
|
device->cur_render_side = side;
|
2013-10-12 16:28:10 -07:00
|
|
|
device->cur_zstencil_buffer = zs;
|
|
|
|
|
|
|
|
if (!tex)
|
|
|
|
return set_current_fbo(device, NULL);
|
|
|
|
|
2018-08-31 14:07:00 -07:00
|
|
|
fbo = get_fbo_by_tex(tex);
|
2013-10-12 16:28:10 -07:00
|
|
|
if (!fbo)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
set_current_fbo(device, fbo);
|
|
|
|
|
|
|
|
if (!attach_rendertarget(fbo, tex, side))
|
|
|
|
return false;
|
|
|
|
if (!attach_zstencil(fbo, zs))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_set_render_target(gs_device_t *device, gs_texture_t *tex,
|
2019-06-22 22:13:45 -07:00
|
|
|
gs_zstencil_t *zstencil)
|
2013-10-12 16:28:10 -07:00
|
|
|
{
|
2013-10-16 23:31:18 -07:00
|
|
|
if (tex) {
|
|
|
|
if (tex->type != GS_TEXTURE_2D) {
|
|
|
|
blog(LOG_ERROR, "Texture is not a 2D texture");
|
|
|
|
goto fail;
|
|
|
|
}
|
2013-10-12 16:28:10 -07:00
|
|
|
|
2013-10-16 23:31:18 -07:00
|
|
|
if (!tex->is_render_target) {
|
|
|
|
blog(LOG_ERROR, "Texture is not a render target");
|
|
|
|
goto fail;
|
|
|
|
}
|
2013-10-12 16:28:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!set_target(device, tex, 0, zstencil))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
fail:
|
2014-08-07 23:42:07 -07:00
|
|
|
blog(LOG_ERROR, "device_set_render_target (GL) failed");
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_set_cube_render_target(gs_device_t *device, gs_texture_t *cubetex,
|
2019-06-22 22:13:45 -07:00
|
|
|
int side, gs_zstencil_t *zstencil)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-16 23:31:18 -07:00
|
|
|
if (cubetex) {
|
|
|
|
if (cubetex->type != GS_TEXTURE_CUBE) {
|
|
|
|
blog(LOG_ERROR, "Texture is not a cube texture");
|
|
|
|
goto fail;
|
|
|
|
}
|
2013-10-12 16:28:10 -07:00
|
|
|
|
2013-10-16 23:31:18 -07:00
|
|
|
if (!cubetex->is_render_target) {
|
|
|
|
blog(LOG_ERROR, "Texture is not a render target");
|
|
|
|
goto fail;
|
|
|
|
}
|
2013-10-12 16:28:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!set_target(device, cubetex, side, zstencil))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
fail:
|
2014-08-07 23:42:07 -07:00
|
|
|
blog(LOG_ERROR, "device_set_cube_render_target (GL) failed");
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
void device_copy_texture_region(gs_device_t *device, gs_texture_t *dst,
|
|
|
|
uint32_t dst_x, uint32_t dst_y,
|
|
|
|
gs_texture_t *src, uint32_t src_x,
|
|
|
|
uint32_t src_y, uint32_t src_w, uint32_t src_h)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
struct gs_texture_2d *src2d = (struct gs_texture_2d *)src;
|
|
|
|
struct gs_texture_2d *dst2d = (struct gs_texture_2d *)dst;
|
2013-10-12 16:28:10 -07:00
|
|
|
|
2013-10-16 23:31:18 -07:00
|
|
|
if (!src) {
|
|
|
|
blog(LOG_ERROR, "Source texture is NULL");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dst) {
|
|
|
|
blog(LOG_ERROR, "Destination texture is NULL");
|
2013-10-12 16:28:10 -07:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dst->type != GS_TEXTURE_2D || src->type != GS_TEXTURE_2D) {
|
|
|
|
blog(LOG_ERROR, "Source and destination textures must be 2D "
|
2019-06-22 22:13:45 -07:00
|
|
|
"textures");
|
2013-10-12 16:28:10 -07:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2013-10-16 23:31:18 -07:00
|
|
|
if (dst->format != src->format) {
|
|
|
|
blog(LOG_ERROR, "Source and destination formats do not match");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
uint32_t nw = (uint32_t)src_w ? (uint32_t)src_w
|
|
|
|
: (src2d->width - src_x);
|
|
|
|
uint32_t nh = (uint32_t)src_h ? (uint32_t)src_h
|
|
|
|
: (src2d->height - src_y);
|
2014-04-09 11:04:58 -07:00
|
|
|
|
|
|
|
if (dst2d->width - dst_x < nw || dst2d->height - dst_y < nh) {
|
|
|
|
blog(LOG_ERROR, "Destination texture region is not big "
|
2019-06-22 22:13:45 -07:00
|
|
|
"enough to hold the source region");
|
2013-10-12 16:28:10 -07:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2018-08-31 14:07:00 -07:00
|
|
|
if (!gl_copy_texture(device, dst, dst_x, dst_y, src, src_x, src_y, nw,
|
2019-06-22 22:13:45 -07:00
|
|
|
nh))
|
2013-10-12 16:28:10 -07:00
|
|
|
goto fail;
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
blog(LOG_ERROR, "device_copy_texture (GL) failed");
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_copy_texture(gs_device_t *device, gs_texture_t *dst,
|
2019-06-22 22:13:45 -07:00
|
|
|
gs_texture_t *src)
|
2014-04-09 11:04:58 -07:00
|
|
|
{
|
|
|
|
device_copy_texture_region(device, dst, 0, 0, src, 0, 0, 0, 0);
|
|
|
|
}
|
|
|
|
|
2019-10-10 21:06:01 -07:00
|
|
|
void device_begin_frame(gs_device_t *device)
|
|
|
|
{
|
|
|
|
/* does nothing */
|
|
|
|
UNUSED_PARAMETER(device);
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_begin_scene(gs_device_t *device)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 20:18:05 -07:00
|
|
|
clear_textures(device);
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2019-06-02 06:49:38 -07:00
|
|
|
static inline bool can_render(const gs_device_t *device, uint32_t num_verts)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 23:53:30 -07:00
|
|
|
if (!device->cur_vertex_shader) {
|
|
|
|
blog(LOG_ERROR, "No vertex shader specified");
|
2013-10-12 20:18:05 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-10-12 23:53:30 -07:00
|
|
|
if (!device->cur_pixel_shader) {
|
|
|
|
blog(LOG_ERROR, "No pixel shader specified");
|
2013-10-12 20:18:05 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-06-02 06:49:38 -07:00
|
|
|
if (!device->cur_vertex_buffer && (num_verts == 0)) {
|
2013-10-12 20:18:05 -07:00
|
|
|
blog(LOG_ERROR, "No vertex buffer specified");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-07-30 17:28:40 -07:00
|
|
|
if (!device->cur_swap && !device->cur_render_target) {
|
|
|
|
blog(LOG_ERROR, "No active swap chain or render target");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-10-12 20:18:05 -07:00
|
|
|
return true;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2013-10-12 23:53:30 -07:00
|
|
|
static void update_viewproj_matrix(struct gs_device *device)
|
|
|
|
{
|
|
|
|
struct gs_shader *vs = device->cur_vertex_shader;
|
2015-02-26 20:01:52 -08:00
|
|
|
struct matrix4 cur_proj;
|
|
|
|
|
2014-06-14 23:09:13 -07:00
|
|
|
gs_matrix_get(&device->cur_view);
|
2015-02-26 20:01:52 -08:00
|
|
|
matrix4_copy(&cur_proj, &device->cur_proj);
|
|
|
|
|
|
|
|
if (device->cur_fbo) {
|
|
|
|
cur_proj.x.y = -cur_proj.x.y;
|
|
|
|
cur_proj.y.y = -cur_proj.y.y;
|
|
|
|
cur_proj.z.y = -cur_proj.z.y;
|
|
|
|
cur_proj.t.y = -cur_proj.t.y;
|
|
|
|
|
|
|
|
glFrontFace(GL_CW);
|
|
|
|
} else {
|
|
|
|
glFrontFace(GL_CCW);
|
|
|
|
}
|
|
|
|
|
|
|
|
gl_success("glFrontFace");
|
2013-10-12 23:53:30 -07:00
|
|
|
|
2015-02-26 20:01:52 -08:00
|
|
|
matrix4_mul(&device->cur_viewproj, &device->cur_view, &cur_proj);
|
2013-10-12 23:53:30 -07:00
|
|
|
matrix4_transpose(&device->cur_viewproj, &device->cur_viewproj);
|
|
|
|
|
|
|
|
if (vs->viewproj)
|
2014-08-07 23:42:07 -07:00
|
|
|
gs_shader_set_matrix4(vs->viewproj, &device->cur_viewproj);
|
2013-10-12 23:53:30 -07:00
|
|
|
}
|
|
|
|
|
2014-09-26 15:25:59 -07:00
|
|
|
static inline struct gs_program *find_program(const struct gs_device *device)
|
2013-10-16 23:31:18 -07:00
|
|
|
{
|
2014-09-19 04:33:49 -07:00
|
|
|
struct gs_program *program = device->first_program;
|
2013-10-16 23:31:18 -07:00
|
|
|
|
2014-09-19 04:33:49 -07:00
|
|
|
while (program) {
|
|
|
|
if (program->vertex_shader == device->cur_vertex_shader &&
|
2019-06-22 22:13:45 -07:00
|
|
|
program->pixel_shader == device->cur_pixel_shader)
|
2014-09-19 04:33:49 -07:00
|
|
|
return program;
|
2013-10-16 23:31:18 -07:00
|
|
|
|
2014-09-19 04:33:49 -07:00
|
|
|
program = program->next;
|
|
|
|
}
|
2013-10-16 23:31:18 -07:00
|
|
|
|
2014-09-19 04:33:49 -07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline struct gs_program *get_shader_program(struct gs_device *device)
|
|
|
|
{
|
|
|
|
struct gs_program *program = find_program(device);
|
|
|
|
|
|
|
|
if (!program)
|
|
|
|
program = gs_program_create(device);
|
2013-10-16 23:31:18 -07:00
|
|
|
|
2014-09-19 04:33:49 -07:00
|
|
|
return program;
|
2013-10-16 23:31:18 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_draw(gs_device_t *device, enum gs_draw_mode draw_mode,
|
2019-06-22 22:13:45 -07:00
|
|
|
uint32_t start_vert, uint32_t num_verts)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2019-06-24 21:13:04 -07:00
|
|
|
struct gs_vertex_buffer *vb = device->cur_vertex_buffer;
|
2013-10-12 20:18:05 -07:00
|
|
|
struct gs_index_buffer *ib = device->cur_index_buffer;
|
2019-06-22 22:13:45 -07:00
|
|
|
GLenum topology = convert_gs_topology(draw_mode);
|
2014-09-25 17:44:05 -07:00
|
|
|
gs_effect_t *effect = gs_get_effect();
|
2014-09-19 04:33:49 -07:00
|
|
|
struct gs_program *program;
|
2013-10-12 20:18:05 -07:00
|
|
|
|
2019-06-02 06:49:38 -07:00
|
|
|
if (!can_render(device, num_verts))
|
2013-10-12 20:18:05 -07:00
|
|
|
goto fail;
|
|
|
|
|
2013-10-16 23:31:18 -07:00
|
|
|
if (effect)
|
2014-08-07 23:42:07 -07:00
|
|
|
gs_effect_update_params(effect);
|
2013-10-16 23:31:18 -07:00
|
|
|
|
2014-09-19 04:33:49 -07:00
|
|
|
program = get_shader_program(device);
|
|
|
|
if (!program)
|
|
|
|
goto fail;
|
2013-10-16 23:31:18 -07:00
|
|
|
|
2019-06-24 21:13:04 -07:00
|
|
|
if (vb)
|
|
|
|
load_vb_buffers(program, vb, ib);
|
|
|
|
else
|
|
|
|
gl_bind_vertex_array(device->empty_vao);
|
2013-10-12 23:53:30 -07:00
|
|
|
|
2014-09-19 04:33:49 -07:00
|
|
|
if (program != device->cur_program && device->cur_program) {
|
|
|
|
glUseProgram(0);
|
|
|
|
gl_success("glUseProgram (zero)");
|
|
|
|
}
|
2013-10-16 23:31:18 -07:00
|
|
|
|
2014-09-19 04:33:49 -07:00
|
|
|
if (program != device->cur_program) {
|
|
|
|
device->cur_program = program;
|
|
|
|
|
|
|
|
glUseProgram(program->obj);
|
|
|
|
if (!gl_success("glUseProgram"))
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
update_viewproj_matrix(device);
|
|
|
|
|
|
|
|
program_update_params(program);
|
2013-10-16 23:31:18 -07:00
|
|
|
|
2013-10-12 20:18:05 -07:00
|
|
|
if (ib) {
|
2013-10-16 23:31:18 -07:00
|
|
|
if (num_verts == 0)
|
|
|
|
num_verts = (uint32_t)device->cur_index_buffer->num;
|
2013-10-12 20:18:05 -07:00
|
|
|
glDrawElements(topology, num_verts, ib->gl_type,
|
2019-06-22 22:13:45 -07:00
|
|
|
(const GLvoid *)(start_vert * ib->width));
|
2013-10-12 20:18:05 -07:00
|
|
|
if (!gl_success("glDrawElements"))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
} else {
|
2013-10-16 23:31:18 -07:00
|
|
|
if (num_verts == 0)
|
|
|
|
num_verts = (uint32_t)device->cur_vertex_buffer->num;
|
2013-10-12 20:18:05 -07:00
|
|
|
glDrawArrays(topology, start_vert, num_verts);
|
|
|
|
if (!gl_success("glDrawArrays"))
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
blog(LOG_ERROR, "device_draw (GL) failed");
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_end_scene(gs_device_t *device)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 20:18:05 -07:00
|
|
|
/* does nothing */
|
2014-02-14 14:13:36 -08:00
|
|
|
UNUSED_PARAMETER(device);
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_clear(gs_device_t *device, uint32_t clear_flags,
|
2019-06-22 22:13:45 -07:00
|
|
|
const struct vec4 *color, float depth, uint8_t stencil)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 20:18:05 -07:00
|
|
|
GLbitfield gl_flags = 0;
|
2013-10-02 23:14:15 -07:00
|
|
|
|
2013-10-12 20:18:05 -07:00
|
|
|
if (clear_flags & GS_CLEAR_COLOR) {
|
2013-10-16 23:31:18 -07:00
|
|
|
glClearColor(color->x, color->y, color->z, color->w);
|
2013-10-12 20:18:05 -07:00
|
|
|
gl_flags |= GL_COLOR_BUFFER_BIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (clear_flags & GS_CLEAR_DEPTH) {
|
|
|
|
glClearDepth(depth);
|
|
|
|
gl_flags |= GL_DEPTH_BUFFER_BIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (clear_flags & GS_CLEAR_STENCIL) {
|
|
|
|
glClearStencil(stencil);
|
|
|
|
gl_flags |= GL_STENCIL_BUFFER_BIT;
|
|
|
|
}
|
|
|
|
|
2013-10-16 23:31:18 -07:00
|
|
|
glClear(gl_flags);
|
2013-10-12 20:18:05 -07:00
|
|
|
if (!gl_success("glClear"))
|
|
|
|
blog(LOG_ERROR, "device_clear (GL) failed");
|
2014-02-14 14:13:36 -08:00
|
|
|
|
|
|
|
UNUSED_PARAMETER(device);
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_flush(gs_device_t *device)
|
2014-06-25 19:32:34 -07:00
|
|
|
{
|
2015-07-30 17:59:34 -07:00
|
|
|
#ifdef __APPLE__
|
|
|
|
if (!device->cur_swap)
|
|
|
|
glFlush();
|
|
|
|
#else
|
2014-06-25 19:32:34 -07:00
|
|
|
glFlush();
|
2015-07-30 17:59:34 -07:00
|
|
|
#endif
|
2014-06-25 21:28:20 -07:00
|
|
|
|
|
|
|
UNUSED_PARAMETER(device);
|
2014-06-25 19:32:34 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_set_cull_mode(gs_device_t *device, enum gs_cull_mode mode)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 20:18:05 -07:00
|
|
|
if (device->cur_cull_mode == mode)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (device->cur_cull_mode == GS_NEITHER)
|
|
|
|
gl_enable(GL_CULL_FACE);
|
|
|
|
|
|
|
|
device->cur_cull_mode = mode;
|
2013-10-16 23:31:18 -07:00
|
|
|
|
2013-10-12 20:18:05 -07:00
|
|
|
if (mode == GS_BACK)
|
2013-10-16 23:31:18 -07:00
|
|
|
gl_cull_face(GL_BACK);
|
2013-10-12 20:18:05 -07:00
|
|
|
else if (mode == GS_FRONT)
|
2013-10-16 23:31:18 -07:00
|
|
|
gl_cull_face(GL_FRONT);
|
2013-10-12 20:18:05 -07:00
|
|
|
else
|
|
|
|
gl_disable(GL_CULL_FACE);
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-26 15:25:59 -07:00
|
|
|
enum gs_cull_mode device_get_cull_mode(const gs_device_t *device)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 20:18:05 -07:00
|
|
|
return device->cur_cull_mode;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_enable_blending(gs_device_t *device, bool enable)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 20:18:05 -07:00
|
|
|
if (enable)
|
|
|
|
gl_enable(GL_BLEND);
|
|
|
|
else
|
|
|
|
gl_disable(GL_BLEND);
|
2014-02-14 14:13:36 -08:00
|
|
|
|
|
|
|
UNUSED_PARAMETER(device);
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_enable_depth_test(gs_device_t *device, bool enable)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 20:18:05 -07:00
|
|
|
if (enable)
|
|
|
|
gl_enable(GL_DEPTH_TEST);
|
|
|
|
else
|
|
|
|
gl_disable(GL_DEPTH_TEST);
|
2014-02-14 14:13:36 -08:00
|
|
|
|
|
|
|
UNUSED_PARAMETER(device);
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_enable_stencil_test(gs_device_t *device, bool enable)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 20:18:05 -07:00
|
|
|
if (enable)
|
|
|
|
gl_enable(GL_STENCIL_TEST);
|
|
|
|
else
|
|
|
|
gl_disable(GL_STENCIL_TEST);
|
2014-02-14 14:13:36 -08:00
|
|
|
|
|
|
|
UNUSED_PARAMETER(device);
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_enable_stencil_write(gs_device_t *device, bool enable)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 20:18:05 -07:00
|
|
|
if (enable)
|
|
|
|
glStencilMask(0xFFFFFFFF);
|
|
|
|
else
|
|
|
|
glStencilMask(0);
|
2014-02-14 14:13:36 -08:00
|
|
|
|
|
|
|
UNUSED_PARAMETER(device);
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
void device_enable_color(gs_device_t *device, bool red, bool green, bool blue,
|
|
|
|
bool alpha)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 20:18:05 -07:00
|
|
|
glColorMask(red, green, blue, alpha);
|
2014-02-14 14:13:36 -08:00
|
|
|
|
|
|
|
UNUSED_PARAMETER(device);
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_blend_function(gs_device_t *device, enum gs_blend_type src,
|
2019-06-22 22:13:45 -07:00
|
|
|
enum gs_blend_type dest)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 20:18:05 -07:00
|
|
|
GLenum gl_src = convert_gs_blend_type(src);
|
|
|
|
GLenum gl_dst = convert_gs_blend_type(dest);
|
|
|
|
|
|
|
|
glBlendFunc(gl_src, gl_dst);
|
|
|
|
if (!gl_success("glBlendFunc"))
|
2014-08-07 23:42:07 -07:00
|
|
|
blog(LOG_ERROR, "device_blend_function (GL) failed");
|
2014-02-14 14:13:36 -08:00
|
|
|
|
|
|
|
UNUSED_PARAMETER(device);
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2015-03-27 11:18:02 -07:00
|
|
|
void device_blend_function_separate(gs_device_t *device,
|
2019-06-22 22:13:45 -07:00
|
|
|
enum gs_blend_type src_c,
|
|
|
|
enum gs_blend_type dest_c,
|
|
|
|
enum gs_blend_type src_a,
|
|
|
|
enum gs_blend_type dest_a)
|
2015-03-27 11:18:02 -07:00
|
|
|
{
|
|
|
|
GLenum gl_src_c = convert_gs_blend_type(src_c);
|
|
|
|
GLenum gl_dst_c = convert_gs_blend_type(dest_c);
|
|
|
|
GLenum gl_src_a = convert_gs_blend_type(src_a);
|
|
|
|
GLenum gl_dst_a = convert_gs_blend_type(dest_a);
|
|
|
|
|
|
|
|
glBlendFuncSeparate(gl_src_c, gl_dst_c, gl_src_a, gl_dst_a);
|
|
|
|
if (!gl_success("glBlendFuncSeparate"))
|
|
|
|
blog(LOG_ERROR, "device_blend_function_separate (GL) failed");
|
|
|
|
|
|
|
|
UNUSED_PARAMETER(device);
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_depth_function(gs_device_t *device, enum gs_depth_test test)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 20:18:05 -07:00
|
|
|
GLenum gl_test = convert_gs_depth_test(test);
|
|
|
|
|
|
|
|
glDepthFunc(gl_test);
|
|
|
|
if (!gl_success("glDepthFunc"))
|
2014-08-07 23:42:07 -07:00
|
|
|
blog(LOG_ERROR, "device_depth_function (GL) failed");
|
2014-02-14 14:13:36 -08:00
|
|
|
|
|
|
|
UNUSED_PARAMETER(device);
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_stencil_function(gs_device_t *device, enum gs_stencil_side side,
|
2019-06-22 22:13:45 -07:00
|
|
|
enum gs_depth_test test)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 20:18:05 -07:00
|
|
|
GLenum gl_side = convert_gs_stencil_side(side);
|
|
|
|
GLenum gl_test = convert_gs_depth_test(test);
|
|
|
|
|
|
|
|
glStencilFuncSeparate(gl_side, gl_test, 0, 0xFFFFFFFF);
|
|
|
|
if (!gl_success("glStencilFuncSeparate"))
|
2014-08-07 23:42:07 -07:00
|
|
|
blog(LOG_ERROR, "device_stencil_function (GL) failed");
|
2014-02-14 14:13:36 -08:00
|
|
|
|
|
|
|
UNUSED_PARAMETER(device);
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_stencil_op(gs_device_t *device, enum gs_stencil_side side,
|
2019-06-22 22:13:45 -07:00
|
|
|
enum gs_stencil_op_type fail,
|
|
|
|
enum gs_stencil_op_type zfail,
|
|
|
|
enum gs_stencil_op_type zpass)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
GLenum gl_side = convert_gs_stencil_side(side);
|
|
|
|
GLenum gl_fail = convert_gs_stencil_op(fail);
|
2013-10-12 20:18:05 -07:00
|
|
|
GLenum gl_zfail = convert_gs_stencil_op(zfail);
|
|
|
|
GLenum gl_zpass = convert_gs_stencil_op(zpass);
|
|
|
|
|
|
|
|
glStencilOpSeparate(gl_side, gl_fail, gl_zfail, gl_zpass);
|
|
|
|
if (!gl_success("glStencilOpSeparate"))
|
2014-08-07 23:42:07 -07:00
|
|
|
blog(LOG_ERROR, "device_stencil_op (GL) failed");
|
2014-02-14 14:13:36 -08:00
|
|
|
|
|
|
|
UNUSED_PARAMETER(device);
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-26 15:25:59 -07:00
|
|
|
static inline uint32_t get_target_height(const struct gs_device *device)
|
2013-10-16 23:31:18 -07:00
|
|
|
{
|
|
|
|
if (!device->cur_render_target)
|
2014-08-07 23:42:07 -07:00
|
|
|
return device_get_height(device);
|
2013-10-16 23:31:18 -07:00
|
|
|
|
|
|
|
if (device->cur_render_target->type == GS_TEXTURE_2D)
|
2014-08-07 23:42:07 -07:00
|
|
|
return gs_texture_get_height(device->cur_render_target);
|
2013-10-16 23:31:18 -07:00
|
|
|
else /* cube map */
|
2014-08-07 23:42:07 -07:00
|
|
|
return gs_cubetexture_get_size(device->cur_render_target);
|
2013-10-16 23:31:18 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_set_viewport(gs_device_t *device, int x, int y, int width,
|
2019-06-22 22:13:45 -07:00
|
|
|
int height)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2017-05-12 01:41:07 -07:00
|
|
|
uint32_t base_height = 0;
|
|
|
|
int gl_y = 0;
|
2013-10-16 23:31:18 -07:00
|
|
|
|
|
|
|
/* GL uses bottom-up coordinates for viewports. We want top-down */
|
|
|
|
if (device->cur_render_target) {
|
|
|
|
base_height = get_target_height(device);
|
2017-05-12 01:41:07 -07:00
|
|
|
} else if (device->cur_swap) {
|
2013-10-16 23:31:18 -07:00
|
|
|
uint32_t dw;
|
|
|
|
gl_getclientsize(device->cur_swap, &dw, &base_height);
|
|
|
|
}
|
|
|
|
|
2017-05-12 01:41:07 -07:00
|
|
|
if (base_height)
|
|
|
|
gl_y = base_height - y - height;
|
|
|
|
|
|
|
|
glViewport(x, gl_y, width, height);
|
2013-10-12 20:18:05 -07:00
|
|
|
if (!gl_success("glViewport"))
|
2014-08-07 23:42:07 -07:00
|
|
|
blog(LOG_ERROR, "device_set_viewport (GL) failed");
|
2013-10-12 20:18:05 -07:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
device->cur_viewport.x = x;
|
|
|
|
device->cur_viewport.y = y;
|
2013-10-12 20:18:05 -07:00
|
|
|
device->cur_viewport.cx = width;
|
|
|
|
device->cur_viewport.cy = height;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-26 15:25:59 -07:00
|
|
|
void device_get_viewport(const gs_device_t *device, struct gs_rect *rect)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 20:18:05 -07:00
|
|
|
*rect = device->cur_viewport;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-26 15:25:59 -07:00
|
|
|
void device_set_scissor_rect(gs_device_t *device, const struct gs_rect *rect)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2014-02-14 14:13:36 -08:00
|
|
|
UNUSED_PARAMETER(device);
|
2014-05-01 11:26:17 -07:00
|
|
|
|
|
|
|
if (rect != NULL) {
|
|
|
|
glScissor(rect->x, rect->y, rect->cx, rect->cy);
|
|
|
|
if (gl_success("glScissor") && gl_enable(GL_SCISSOR_TEST))
|
|
|
|
return;
|
|
|
|
|
|
|
|
} else if (gl_disable(GL_SCISSOR_TEST)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-07 23:42:07 -07:00
|
|
|
blog(LOG_ERROR, "device_set_scissor_rect (GL) failed");
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
void device_ortho(gs_device_t *device, float left, float right, float top,
|
|
|
|
float bottom, float near, float far)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-16 23:31:18 -07:00
|
|
|
struct matrix4 *dst = &device->cur_proj;
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
float rml = right - left;
|
|
|
|
float bmt = bottom - top;
|
|
|
|
float fmn = far - near;
|
2013-10-16 23:31:18 -07:00
|
|
|
|
|
|
|
vec4_zero(&dst->x);
|
|
|
|
vec4_zero(&dst->y);
|
|
|
|
vec4_zero(&dst->z);
|
|
|
|
vec4_zero(&dst->t);
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
dst->x.x = 2.0f / rml;
|
|
|
|
dst->t.x = (left + right) / -rml;
|
2013-10-16 23:31:18 -07:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
dst->y.y = 2.0f / -bmt;
|
|
|
|
dst->t.y = (bottom + top) / bmt;
|
2013-10-16 23:31:18 -07:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
dst->z.z = -2.0f / fmn;
|
|
|
|
dst->t.z = (far + near) / -fmn;
|
2013-10-16 23:31:18 -07:00
|
|
|
|
|
|
|
dst->t.w = 1.0f;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
void device_frustum(gs_device_t *device, float left, float right, float top,
|
|
|
|
float bottom, float near, float far)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-16 23:31:18 -07:00
|
|
|
struct matrix4 *dst = &device->cur_proj;
|
2013-10-02 23:14:15 -07:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
float rml = right - left;
|
|
|
|
float tmb = top - bottom;
|
|
|
|
float nmf = near - far;
|
|
|
|
float nearx2 = 2.0f * near;
|
2013-10-16 23:31:18 -07:00
|
|
|
|
|
|
|
vec4_zero(&dst->x);
|
|
|
|
vec4_zero(&dst->y);
|
|
|
|
vec4_zero(&dst->z);
|
|
|
|
vec4_zero(&dst->t);
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
dst->x.x = nearx2 / rml;
|
|
|
|
dst->z.x = (left + right) / rml;
|
|
|
|
|
|
|
|
dst->y.y = nearx2 / tmb;
|
|
|
|
dst->z.y = (bottom + top) / tmb;
|
2013-10-16 23:31:18 -07:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
dst->z.z = (far + near) / nmf;
|
|
|
|
dst->t.z = 2.0f * (near * far) / nmf;
|
2013-10-16 23:31:18 -07:00
|
|
|
|
|
|
|
dst->z.w = -1.0f;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_projection_push(gs_device_t *device)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 20:18:05 -07:00
|
|
|
da_push_back(device->proj_stack, &device->cur_proj);
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void device_projection_pop(gs_device_t *device)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 20:18:05 -07:00
|
|
|
struct matrix4 *end;
|
|
|
|
if (!device->proj_stack.num)
|
|
|
|
return;
|
|
|
|
|
|
|
|
end = da_end(device->proj_stack);
|
|
|
|
device->cur_proj = *end;
|
|
|
|
da_pop_back(device->proj_stack);
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
void device_debug_marker_begin(gs_device_t *device, const char *markername,
|
|
|
|
const float color[4])
|
2019-04-02 23:22:19 -07:00
|
|
|
{
|
|
|
|
UNUSED_PARAMETER(device);
|
|
|
|
UNUSED_PARAMETER(color);
|
|
|
|
|
|
|
|
glPushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION, 0, -1, markername);
|
|
|
|
}
|
|
|
|
|
|
|
|
void device_debug_marker_end(gs_device_t *device)
|
|
|
|
{
|
|
|
|
UNUSED_PARAMETER(device);
|
|
|
|
|
|
|
|
glPopDebugGroupKHR();
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void gs_swapchain_destroy(gs_swapchain_t *swapchain)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-12 20:18:05 -07:00
|
|
|
if (!swapchain)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (swapchain->device->cur_swap == swapchain)
|
|
|
|
device_load_swapchain(swapchain->device, NULL);
|
|
|
|
|
2014-04-12 04:33:47 -07:00
|
|
|
gl_platform_cleanup_swapchain(swapchain);
|
|
|
|
|
2013-10-12 20:18:05 -07:00
|
|
|
gl_windowinfo_destroy(swapchain->wi);
|
|
|
|
bfree(swapchain);
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-26 15:25:59 -07:00
|
|
|
uint32_t gs_voltexture_get_width(const gs_texture_t *voltex)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-11 20:14:26 -07:00
|
|
|
/* TODO */
|
2014-02-14 14:13:36 -08:00
|
|
|
UNUSED_PARAMETER(voltex);
|
2013-10-11 20:14:26 -07:00
|
|
|
return 0;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-26 15:25:59 -07:00
|
|
|
uint32_t gs_voltexture_get_height(const gs_texture_t *voltex)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-11 20:14:26 -07:00
|
|
|
/* TODO */
|
2014-02-14 14:13:36 -08:00
|
|
|
UNUSED_PARAMETER(voltex);
|
2013-10-11 20:14:26 -07:00
|
|
|
return 0;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2015-10-21 07:43:02 -07:00
|
|
|
uint32_t gs_voltexture_get_depth(const gs_texture_t *voltex)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-11 20:14:26 -07:00
|
|
|
/* TODO */
|
2014-02-14 14:13:36 -08:00
|
|
|
UNUSED_PARAMETER(voltex);
|
2013-10-11 20:14:26 -07:00
|
|
|
return 0;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-26 15:25:59 -07:00
|
|
|
enum gs_color_format gs_voltexture_get_color_format(const gs_texture_t *voltex)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2013-10-11 20:14:26 -07:00
|
|
|
/* TODO */
|
2014-02-14 14:13:36 -08:00
|
|
|
UNUSED_PARAMETER(voltex);
|
2013-10-11 20:14:26 -07:00
|
|
|
return GS_UNKNOWN;
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void gs_samplerstate_destroy(gs_samplerstate_t *samplerstate)
|
2013-10-02 23:14:15 -07:00
|
|
|
{
|
2014-04-18 20:21:00 -07:00
|
|
|
if (!samplerstate)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (samplerstate->device)
|
|
|
|
for (int i = 0; i < GS_MAX_TEXTURES; i++)
|
|
|
|
if (samplerstate->device->cur_samplers[i] ==
|
2019-06-22 22:13:45 -07:00
|
|
|
samplerstate)
|
2014-04-18 20:21:00 -07:00
|
|
|
samplerstate->device->cur_samplers[i] = NULL;
|
|
|
|
|
2013-10-12 12:35:38 -07:00
|
|
|
samplerstate_release(samplerstate);
|
2013-10-02 23:14:15 -07:00
|
|
|
}
|
2019-07-27 13:31:07 -07:00
|
|
|
|
|
|
|
void gs_timer_destroy(gs_timer_t *timer)
|
|
|
|
{
|
|
|
|
if (!timer)
|
|
|
|
return;
|
|
|
|
|
|
|
|
glDeleteQueries(2, timer->queries);
|
|
|
|
gl_success("glDeleteQueries");
|
|
|
|
|
|
|
|
bfree(timer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void gs_timer_begin(gs_timer_t *timer)
|
|
|
|
{
|
|
|
|
glQueryCounter(timer->queries[0], GL_TIMESTAMP);
|
|
|
|
gl_success("glQueryCounter");
|
|
|
|
}
|
|
|
|
|
|
|
|
void gs_timer_end(gs_timer_t *timer)
|
|
|
|
{
|
|
|
|
glQueryCounter(timer->queries[1], GL_TIMESTAMP);
|
|
|
|
gl_success("glQueryCounter");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool gs_timer_get_data(gs_timer_t *timer, uint64_t *ticks)
|
|
|
|
{
|
|
|
|
GLint available = 0;
|
|
|
|
glGetQueryObjectiv(timer->queries[1], GL_QUERY_RESULT_AVAILABLE,
|
|
|
|
&available);
|
|
|
|
|
|
|
|
GLuint64 begin, end;
|
|
|
|
glGetQueryObjectui64v(timer->queries[0], GL_QUERY_RESULT, &begin);
|
|
|
|
gl_success("glGetQueryObjectui64v");
|
|
|
|
glGetQueryObjectui64v(timer->queries[1], GL_QUERY_RESULT, &end);
|
|
|
|
gl_success("glGetQueryObjectui64v");
|
|
|
|
|
|
|
|
*ticks = end - begin;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-08-17 11:02:03 -07:00
|
|
|
void gs_timer_range_destroy(gs_timer_range_t *range)
|
|
|
|
{
|
|
|
|
UNUSED_PARAMETER(range);
|
|
|
|
}
|
2019-07-27 13:31:07 -07:00
|
|
|
|
2019-08-17 11:02:03 -07:00
|
|
|
void gs_timer_range_begin(gs_timer_range_t *range)
|
|
|
|
{
|
|
|
|
UNUSED_PARAMETER(range);
|
|
|
|
}
|
2019-07-27 13:31:07 -07:00
|
|
|
|
2019-08-17 11:02:03 -07:00
|
|
|
void gs_timer_range_end(gs_timer_range_t *range)
|
|
|
|
{
|
|
|
|
UNUSED_PARAMETER(range);
|
|
|
|
}
|
2019-07-27 13:31:07 -07:00
|
|
|
|
|
|
|
bool gs_timer_range_get_data(gs_timer_range_t *range, bool *disjoint,
|
|
|
|
uint64_t *frequency)
|
|
|
|
{
|
2019-08-17 11:02:03 -07:00
|
|
|
UNUSED_PARAMETER(range);
|
|
|
|
|
2019-07-27 13:31:07 -07:00
|
|
|
*disjoint = false;
|
|
|
|
*frequency = 1000000000;
|
|
|
|
return true;
|
|
|
|
}
|