0
0
mirror of https://github.com/obsproject/obs-studio.git synced 2024-09-20 13:08:50 +02:00
obs-studio/libobs/graphics/graphics.c

1940 lines
45 KiB
C
Raw Normal View History

2013-10-01 04:37:13 +02:00
/******************************************************************************
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
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
the Free Software Foundation, either version 2 of the License, or
2013-10-01 04:37:13 +02: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/>.
******************************************************************************/
#include <assert.h>
#include "../util/base.h"
#include "../util/bmem.h"
#include "../util/platform.h"
#include "graphics-internal.h"
#include "vec2.h"
#include "vec3.h"
#include "quat.h"
#include "axisang.h"
#include "effect-parser.h"
#include "effect.h"
#ifdef _MSC_VER
static __declspec(thread) graphics_t thread_graphics = NULL;
#else /* assume GCC or that other compiler we dare not mention */
static __thread graphics_t thread_graphics = NULL;
#endif
#define IMMEDIATE_COUNT 512
2013-10-01 04:37:13 +02:00
bool load_graphics_imports(struct gs_exports *exports, void *module,
const char *module_name);
static bool graphics_init_immediate_vb(struct graphics_subsystem *graphics)
2013-10-01 04:37:13 +02:00
{
struct vb_data *vbd;
vbd = vbdata_create();
vbd->num = IMMEDIATE_COUNT;
2013-10-01 16:24:59 +02:00
vbd->points = bmalloc(sizeof(struct vec3)*IMMEDIATE_COUNT);
vbd->normals = bmalloc(sizeof(struct vec3)*IMMEDIATE_COUNT);
vbd->colors = bmalloc(sizeof(uint32_t) *IMMEDIATE_COUNT);
2013-10-01 04:37:13 +02:00
vbd->num_tex = 1;
2013-10-01 16:24:59 +02:00
vbd->tvarray = bmalloc(sizeof(struct tvertarray));
2013-10-01 04:37:13 +02:00
vbd->tvarray[0].width = 2;
vbd->tvarray[0].array =
2013-10-01 16:24:59 +02:00
bmalloc(sizeof(struct vec2) * IMMEDIATE_COUNT);
2013-10-01 04:37:13 +02:00
graphics->immediate_vertbuffer = graphics->exports.
device_create_vertexbuffer(graphics->device, vbd, GS_DYNAMIC);
if (!graphics->immediate_vertbuffer)
return false;
return true;
}
static bool graphics_init_sprite_vb(struct graphics_subsystem *graphics)
{
struct vb_data *vbd;
2013-10-01 04:37:13 +02:00
vbd = vbdata_create();
vbd->num = 4;
2013-10-01 16:24:59 +02:00
vbd->points = bmalloc(sizeof(struct vec3) * 4);
2013-10-01 04:37:13 +02:00
vbd->num_tex = 1;
2013-10-01 16:24:59 +02:00
vbd->tvarray = bmalloc(sizeof(struct tvertarray));
2013-10-01 04:37:13 +02:00
vbd->tvarray[0].width = 2;
2013-10-01 16:24:59 +02:00
vbd->tvarray[0].array = bmalloc(sizeof(struct vec2) * 4);
2013-10-01 04:37:13 +02:00
memset(vbd->points, 0, sizeof(struct vec3) * 4);
memset(vbd->tvarray[0].array, 0, sizeof(struct vec2) * 4);
graphics->sprite_buffer = graphics->exports.
device_create_vertexbuffer(graphics->device, vbd, GS_DYNAMIC);
if (!graphics->sprite_buffer)
return false;
return true;
}
static bool graphics_init(struct graphics_subsystem *graphics)
{
struct matrix4 top_mat;
matrix4_identity(&top_mat);
da_push_back(graphics->matrix_stack, &top_mat);
graphics->exports.device_entercontext(graphics->device);
if (!graphics_init_immediate_vb(graphics))
return false;
if (!graphics_init_sprite_vb(graphics))
return false;
if (pthread_mutex_init(&graphics->mutex, NULL) != 0)
return false;
graphics->exports.device_leavecontext(graphics->device);
return true;
}
2013-10-01 04:37:13 +02:00
int gs_create(graphics_t *pgraphics, const char *module,
struct gs_init_data *data)
{
int errcode = GS_ERROR_FAIL;
graphics_t graphics = bzalloc(sizeof(struct graphics_subsystem));
pthread_mutex_init_value(&graphics->mutex);
2013-10-01 04:37:13 +02:00
Add source properties window (very preliminary) - Add a properties window for sources so that you can now actually edit the settings for sources. Also, display the source by itself in the window (Note: not working on mac, and possibly not working on linux). When changing the settings for a source, it will call obs_source_update on that source when you have modified any values automatically. - Add a properties 'widget', eventually I want to turn this in to a regular nice properties view like you'd see in the designer, but right now it just uses a form layout in a QScrollArea with regular controls to display the properties. It's clunky but works for the time being. - Make it so that swap chains and the main graphics subsystem will automatically use at least one backbuffer if none was specified - Fix bug where displays weren't added to the main display array - Make it so that you can get the properties of a source via the actual pointer of a source/encoder/output in addition to being able to look up properties via identifier. - When registering source types, check for required functions (wasn't doing it before). getheight/getwidth should not be optional if it's a video source as well. - Add an RAII OBSObj wrapper to obs.hpp for non-reference-counted libobs pointers - Add an RAII OBSSignal wrapper to obs.hpp for libobs signals to automatically disconnect them on destruction - Move the "scale and center" calculation in window-basic-main.cpp to its own function and in its own source file - Add an 'update' callback to WASAPI audio sources
2014-03-23 09:07:54 +01:00
if (!data->num_backbuffers)
data->num_backbuffers = 1;
2013-10-01 04:37:13 +02:00
graphics->module = os_dlopen(module);
if (!graphics->module) {
errcode = GS_ERROR_MODULENOTFOUND;
goto error;
}
if (!load_graphics_imports(&graphics->exports, graphics->module,
module))
goto error;
graphics->device = graphics->exports.device_create(data);
if (!graphics->device)
goto error;
if (!graphics_init(graphics))
goto error;
*pgraphics = graphics;
return GS_SUCCESS;
error:
gs_destroy(graphics);
return errcode;
}
void gs_destroy(graphics_t graphics)
{
if (!graphics)
return;
while (thread_graphics)
gs_leavecontext();
if (graphics->device) {
graphics->exports.device_entercontext(graphics->device);
2013-10-01 04:37:13 +02:00
graphics->exports.vertexbuffer_destroy(graphics->sprite_buffer);
graphics->exports.vertexbuffer_destroy(
graphics->immediate_vertbuffer);
graphics->exports.device_destroy(graphics->device);
}
2013-10-01 04:37:13 +02:00
pthread_mutex_destroy(&graphics->mutex);
2013-10-01 04:37:13 +02:00
da_free(graphics->matrix_stack);
da_free(graphics->viewport_stack);
if (graphics->module)
os_dlclose(graphics->module);
2013-10-01 04:37:13 +02:00
bfree(graphics);
}
2013-10-01 04:37:13 +02:00
void gs_entercontext(graphics_t graphics)
{
2014-02-24 06:39:33 +01:00
if (!graphics) return;
bool is_current = thread_graphics == graphics;
if (thread_graphics && !is_current) {
while (thread_graphics)
gs_leavecontext();
}
if (!is_current) {
pthread_mutex_lock(&graphics->mutex);
graphics->exports.device_entercontext(graphics->device);
thread_graphics = graphics;
}
os_atomic_inc_long(&graphics->ref);
2013-10-01 04:37:13 +02:00
}
void gs_leavecontext(void)
2013-10-01 04:37:13 +02:00
{
if (thread_graphics) {
if (!os_atomic_dec_long(&thread_graphics->ref)) {
graphics_t graphics = thread_graphics;
graphics->exports.device_leavecontext(graphics->device);
pthread_mutex_unlock(&graphics->mutex);
thread_graphics = NULL;
}
}
2013-10-01 04:37:13 +02:00
}
graphics_t gs_getcontext(void)
{
return thread_graphics;
}
static inline struct matrix4 *top_matrix(graphics_t graphics)
2013-10-01 04:37:13 +02:00
{
2014-02-24 06:39:33 +01:00
return graphics ?
(graphics->matrix_stack.array + graphics->cur_matrix) : NULL;
2013-10-01 04:37:13 +02:00
}
void gs_matrix_push(void)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics)
return;
struct matrix4 mat, *top_mat = top_matrix(graphics);
2013-10-01 04:37:13 +02:00
memcpy(&mat, top_mat, sizeof(struct matrix4));
2013-10-01 04:37:13 +02:00
da_push_back(graphics->matrix_stack, &mat);
graphics->cur_matrix++;
}
void gs_matrix_pop(void)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics)
return;
2013-10-01 04:37:13 +02:00
if (graphics->cur_matrix == 0) {
blog(LOG_ERROR, "Tried to pop last matrix on stack");
2013-10-01 04:37:13 +02:00
return;
}
da_erase(graphics->matrix_stack, graphics->cur_matrix);
graphics->cur_matrix--;
}
void gs_matrix_identity(void)
{
struct matrix4 *top_mat = top_matrix(thread_graphics);
2014-02-24 06:39:33 +01:00
if (top_mat)
matrix4_identity(top_mat);
2013-10-01 04:37:13 +02:00
}
void gs_matrix_transpose(void)
{
struct matrix4 *top_mat = top_matrix(thread_graphics);
2014-02-24 06:39:33 +01:00
if (top_mat)
matrix4_transpose(top_mat, top_mat);
2013-10-01 04:37:13 +02:00
}
void gs_matrix_set(const struct matrix4 *matrix)
2013-10-01 04:37:13 +02:00
{
struct matrix4 *top_mat = top_matrix(thread_graphics);
2014-02-24 06:39:33 +01:00
if (top_mat)
matrix4_copy(top_mat, matrix);
2013-10-01 04:37:13 +02:00
}
void gs_matrix_get(struct matrix4 *dst)
2013-10-01 04:37:13 +02:00
{
struct matrix4 *top_mat = top_matrix(thread_graphics);
2014-02-24 06:39:33 +01:00
if (top_mat)
matrix4_copy(dst, top_mat);
2013-10-01 04:37:13 +02:00
}
void gs_matrix_mul(const struct matrix4 *matrix)
2013-10-01 04:37:13 +02:00
{
struct matrix4 *top_mat = top_matrix(thread_graphics);
2014-02-24 06:39:33 +01:00
if (top_mat)
matrix4_mul(top_mat, top_mat, matrix);
2013-10-01 04:37:13 +02:00
}
void gs_matrix_rotquat(const struct quat *rot)
{
struct matrix4 *top_mat = top_matrix(thread_graphics);
2014-02-24 06:39:33 +01:00
if (top_mat)
matrix4_rotate(top_mat, top_mat, rot);
2013-10-01 04:37:13 +02:00
}
void gs_matrix_rotaa(const struct axisang *rot)
{
struct matrix4 *top_mat = top_matrix(thread_graphics);
2014-02-24 06:39:33 +01:00
if (top_mat)
matrix4_rotate_aa(top_mat, top_mat, rot);
2013-10-01 04:37:13 +02:00
}
void gs_matrix_translate(const struct vec3 *pos)
{
struct matrix4 *top_mat = top_matrix(thread_graphics);
2014-02-24 06:39:33 +01:00
if (top_mat)
matrix4_translate3v(top_mat, top_mat, pos);
2013-10-01 04:37:13 +02:00
}
void gs_matrix_scale(const struct vec3 *scale)
{
struct matrix4 *top_mat = top_matrix(thread_graphics);
2014-02-24 06:39:33 +01:00
if (top_mat)
matrix4_scale(top_mat, top_mat, scale);
2013-10-01 04:37:13 +02:00
}
void gs_matrix_rotaa4f(float x, float y, float z, float angle)
{
struct matrix4 *top_mat = top_matrix(thread_graphics);
2013-10-01 04:37:13 +02:00
struct axisang aa;
2014-02-24 06:39:33 +01:00
2014-04-27 08:44:27 +02:00
if (top_mat) {
2014-02-24 06:39:33 +01:00
axisang_set(&aa, x, y, z, angle);
matrix4_rotate_aa(top_mat, top_mat, &aa);
2014-02-24 06:39:33 +01:00
}
2013-10-01 04:37:13 +02:00
}
void gs_matrix_translate3f(float x, float y, float z)
{
struct matrix4 *top_mat = top_matrix(thread_graphics);
2013-10-01 04:37:13 +02:00
struct vec3 p;
2014-02-24 06:39:33 +01:00
if (top_mat) {
vec3_set(&p, x, y, z);
matrix4_translate3v(top_mat, top_mat, &p);
2014-02-24 06:39:33 +01:00
}
2013-10-01 04:37:13 +02:00
}
void gs_matrix_scale3f(float x, float y, float z)
{
struct matrix4 *top_mat = top_matrix(thread_graphics);
2013-10-01 04:37:13 +02:00
struct vec3 p;
2014-02-24 06:39:33 +01:00
if (top_mat) {
vec3_set(&p, x, y, z);
matrix4_scale(top_mat, top_mat, &p);
2014-02-24 06:39:33 +01:00
}
2013-10-01 04:37:13 +02:00
}
static inline void reset_immediate_arrays(graphics_t graphics)
{
da_init(graphics->verts);
da_init(graphics->norms);
da_init(graphics->colors);
2014-02-24 06:39:33 +01:00
for (size_t i = 0; i < 16; i++)
2013-10-01 04:37:13 +02:00
da_init(graphics->texverts[i]);
}
void gs_renderstart(bool b_new)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics)
return;
2013-10-01 04:37:13 +02:00
graphics->using_immediate = !b_new;
reset_immediate_arrays(graphics);
if (b_new) {
graphics->vbd = vbdata_create();
} else {
graphics->vbd = vertexbuffer_getdata(
graphics->immediate_vertbuffer);
memset(graphics->vbd->colors, 0xFF,
sizeof(uint32_t) * IMMEDIATE_COUNT);
2013-10-01 04:37:13 +02:00
graphics->verts.array = graphics->vbd->points;
graphics->norms.array = graphics->vbd->normals;
graphics->colors.array = graphics->vbd->colors;
graphics->texverts[0].array = graphics->vbd->tvarray[0].array;
graphics->verts.capacity = IMMEDIATE_COUNT;
graphics->norms.capacity = IMMEDIATE_COUNT;
graphics->colors.capacity = IMMEDIATE_COUNT;
graphics->texverts[0].capacity = IMMEDIATE_COUNT;
2013-10-01 04:37:13 +02:00
}
}
static inline size_t min_size(const size_t a, const size_t b)
{
return (a < b) ? a : b;
}
void gs_renderstop(enum gs_draw_mode mode)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
size_t i, num;
2013-10-01 04:37:13 +02:00
2014-02-24 06:39:33 +01:00
if (!graphics)
return;
num = graphics->verts.num;
2013-10-01 04:37:13 +02:00
if (!num) {
if (!graphics->using_immediate) {
da_free(graphics->verts);
da_free(graphics->norms);
da_free(graphics->colors);
for (i = 0; i < 16; i++)
da_free(graphics->texverts[i]);
vbdata_destroy(graphics->vbd);
}
return;
}
if (graphics->norms.num &&
(graphics->norms.num != graphics->verts.num)) {
blog(LOG_ERROR, "gs_renderstop: normal count does "
"not match vertex count");
2013-10-01 04:37:13 +02:00
num = min_size(num, graphics->norms.num);
}
if (graphics->colors.num &&
(graphics->colors.num != graphics->verts.num)) {
blog(LOG_ERROR, "gs_renderstop: color count does "
"not match vertex count");
2013-10-01 04:37:13 +02:00
num = min_size(num, graphics->colors.num);
}
if (graphics->texverts[0].num &&
(graphics->texverts[0].num != graphics->verts.num)) {
blog(LOG_ERROR, "gs_renderstop: texture vertex count does "
"not match vertex count");
2013-10-01 04:37:13 +02:00
num = min_size(num, graphics->texverts[0].num);
}
if (graphics->using_immediate) {
vertexbuffer_flush(graphics->immediate_vertbuffer);
2013-10-01 04:37:13 +02:00
gs_load_vertexbuffer(graphics->immediate_vertbuffer);
gs_load_indexbuffer(NULL);
gs_draw(mode, 0, (uint32_t)num);
reset_immediate_arrays(graphics);
} else {
vertbuffer_t vb = gs_rendersave();
gs_load_vertexbuffer(vb);
gs_load_indexbuffer(NULL);
gs_draw(mode, 0, 0);
vertexbuffer_destroy(vb);
}
graphics->vbd = NULL;
}
vertbuffer_t gs_rendersave(void)
{
graphics_t graphics = thread_graphics;
size_t num_tex, i;
2014-02-24 06:39:33 +01:00
if (!graphics)
return NULL;
2013-10-01 04:37:13 +02:00
if (graphics->using_immediate)
return NULL;
if (!graphics->verts.num) {
2013-10-01 04:37:13 +02:00
vbdata_destroy(graphics->vbd);
return NULL;
}
for (num_tex = 0; num_tex < 16; num_tex++)
2013-10-01 04:37:13 +02:00
if (!graphics->texverts[num_tex].num)
break;
graphics->vbd->points = graphics->verts.array;
graphics->vbd->normals = graphics->norms.array;
graphics->vbd->colors = graphics->colors.array;
graphics->vbd->num = graphics->verts.num;
graphics->vbd->num_tex = num_tex;
if (graphics->vbd->num_tex) {
graphics->vbd->tvarray =
bmalloc(sizeof(struct tvertarray) * num_tex);
for (i = 0; i < num_tex; i++) {
graphics->vbd->tvarray[i].width = 2;
graphics->vbd->tvarray[i].array =
graphics->texverts[i].array;
}
2013-10-01 04:37:13 +02:00
}
reset_immediate_arrays(graphics);
return gs_create_vertexbuffer(graphics->vbd, 0);
}
void gs_vertex2f(float x, float y)
{
struct vec3 v3;
vec3_set(&v3, x, y, 0.0f);
gs_vertex3v(&v3);
}
void gs_vertex3f(float x, float y, float z)
{
struct vec3 v3;
vec3_set(&v3, x, y, z);
gs_vertex3v(&v3);
}
void gs_normal3f(float x, float y, float z)
{
struct vec3 v3;
vec3_set(&v3, x, y, z);
gs_normal3v(&v3);
}
static inline bool validvertsize(graphics_t graphics, size_t num,
const char *name)
{
2013-10-18 02:21:42 +02:00
if (graphics->using_immediate && num == IMMEDIATE_COUNT) {
blog(LOG_ERROR, "%s: tried to use over %u "
"for immediate rendering",
name, IMMEDIATE_COUNT);
2013-10-01 04:37:13 +02:00
return false;
}
return true;
}
void gs_color(uint32_t color)
{
graphics_t graphics = thread_graphics;
if (!graphics)
return;
2013-10-01 04:37:13 +02:00
if (!validvertsize(graphics, graphics->colors.num, "gs_color"))
return;
da_push_back(graphics->colors, &color);
}
void gs_texcoord(float x, float y, int unit)
{
struct vec2 v2;
vec2_set(&v2, x, y);
gs_texcoord2v(&v2, unit);
}
void gs_vertex2v(const struct vec2 *v)
{
struct vec3 v3;
vec3_set(&v3, v->x, v->y, 0.0f);
gs_vertex3v(&v3);
}
void gs_vertex3v(const struct vec3 *v)
{
graphics_t graphics = thread_graphics;
if (!graphics)
return;
2013-10-01 04:37:13 +02:00
if (!validvertsize(graphics, graphics->verts.num, "gs_vertex"))
return;
da_push_back(graphics->verts, v);
}
void gs_normal3v(const struct vec3 *v)
{
graphics_t graphics = thread_graphics;
if (!graphics)
return;
2013-10-01 04:37:13 +02:00
if (!validvertsize(graphics, graphics->norms.num, "gs_normal"))
return;
da_push_back(graphics->norms, v);
}
void gs_color4v(const struct vec4 *v)
{
/* TODO */
UNUSED_PARAMETER(v);
2013-10-01 04:37:13 +02:00
}
void gs_texcoord2v(const struct vec2 *v, int unit)
{
graphics_t graphics = thread_graphics;
if (!graphics)
return;
2013-10-01 04:37:13 +02:00
if (!validvertsize(graphics, graphics->texverts[unit].num,
"gs_texcoord"))
return;
da_push_back(graphics->texverts[unit], v);
}
input_t gs_getinput(void)
{
/* TODO */
return NULL;
}
effect_t gs_geteffect(void)
{
2014-02-24 06:39:33 +01:00
return thread_graphics ? thread_graphics->cur_effect : NULL;
2013-10-01 04:37:13 +02:00
}
effect_t gs_create_effect_from_file(const char *file, char **error_string)
{
char *file_string;
effect_t effect = NULL;
2014-02-24 06:39:33 +01:00
if (!thread_graphics || !file)
return NULL;
2013-10-01 04:37:13 +02:00
file_string = os_quick_read_utf8_file(file);
if (!file_string) {
blog(LOG_ERROR, "Could not load effect file '%s'", file);
2013-10-01 04:37:13 +02:00
return NULL;
}
effect = gs_create_effect(file_string, file, error_string);
bfree(file_string);
return effect;
}
effect_t gs_create_effect(const char *effect_string, const char *filename,
char **error_string)
{
2014-02-24 06:39:33 +01:00
if (!thread_graphics || !effect_string)
return NULL;
struct gs_effect *effect = bzalloc(sizeof(struct gs_effect));
2013-10-01 04:37:13 +02:00
struct effect_parser parser;
bool success;
effect->graphics = thread_graphics;
ep_init(&parser);
success = ep_parse(&parser, effect, effect_string, filename);
if (!success) {
if (error_string)
*error_string = error_data_buildstring(
&parser.cfp.error_list);
2013-10-01 04:37:13 +02:00
effect_destroy(effect);
effect = NULL;
}
ep_free(&parser);
return effect;
}
shader_t gs_create_vertexshader_from_file(const char *file, char **error_string)
2013-10-01 04:37:13 +02:00
{
2014-02-24 06:39:33 +01:00
if (!thread_graphics || !file)
return NULL;
2013-10-01 04:37:13 +02:00
char *file_string;
shader_t shader = NULL;
file_string = os_quick_read_utf8_file(file);
if (!file_string) {
blog(LOG_ERROR, "Could not load vertex shader file '%s'",
2013-10-01 04:37:13 +02:00
file);
return NULL;
}
shader = gs_create_vertexshader(file_string, file, error_string);
bfree(file_string);
return shader;
}
shader_t gs_create_pixelshader_from_file(const char *file, char **error_string)
2013-10-01 04:37:13 +02:00
{
char *file_string;
shader_t shader = NULL;
2014-02-24 06:39:33 +01:00
if (!thread_graphics || !file)
return NULL;
2013-10-01 04:37:13 +02:00
file_string = os_quick_read_utf8_file(file);
if (!file_string) {
blog(LOG_ERROR, "Could not load pixel shader file '%s'",
2013-10-01 04:37:13 +02:00
file);
return NULL;
}
shader = gs_create_pixelshader(file_string, file, error_string);
bfree(file_string);
return shader;
}
texture_t gs_create_texture_from_file(const char *file)
2013-10-01 04:37:13 +02:00
{
/* TODO */
UNUSED_PARAMETER(file);
2013-10-01 04:37:13 +02:00
return NULL;
}
static inline void assign_sprite_rect(float *start, float *end, float size,
bool flip)
{
if (!flip) {
*start = 0.0f;
*end = size;
} else {
*start = size;
*end = 0.0f;
}
}
2013-10-27 00:05:51 +02:00
static inline void assign_sprite_uv(float *start, float *end, bool flip)
{
if (!flip) {
*start = 0.0f;
*end = 1.0f;
} else {
*start = 1.0f;
*end = 0.0f;
}
}
static void build_sprite(struct vb_data *data, float fcx, float fcy,
float start_u, float end_u, float start_v, float end_v)
2013-10-27 00:05:51 +02:00
{
struct vec2 *tvarray = data->tvarray[0].array;
vec3_zero(data->points);
vec3_set(data->points+1, fcx, 0.0f, 0.0f);
vec3_set(data->points+2, 0.0f, fcy, 0.0f);
vec3_set(data->points+3, fcx, fcy, 0.0f);
vec2_set(tvarray, start_u, start_v);
vec2_set(tvarray+1, end_u, start_v);
vec2_set(tvarray+2, start_u, end_v);
vec2_set(tvarray+3, end_u, end_v);
}
static inline void build_sprite_norm(struct vb_data *data, float fcx, float fcy,
uint32_t flip)
{
float start_u, end_u;
float start_v, end_v;
assign_sprite_uv(&start_u, &end_u, (flip & GS_FLIP_U) != 0);
assign_sprite_uv(&start_v, &end_v, (flip & GS_FLIP_V) != 0);
build_sprite(data, fcx, fcy, start_u, end_u, start_v, end_v);
}
static inline void build_sprite_rect(struct vb_data *data, texture_t tex,
float fcx, float fcy, uint32_t flip)
{
float start_u, end_u;
float start_v, end_v;
float width = (float)texture_getwidth(tex);
float height = (float)texture_getheight(tex);
assign_sprite_rect(&start_u, &end_u, width, (flip & GS_FLIP_U) != 0);
assign_sprite_rect(&start_v, &end_v, height, (flip & GS_FLIP_V) != 0);
build_sprite(data, fcx, fcy, start_u, end_u, start_v, end_v);
}
void gs_draw_sprite(texture_t tex, uint32_t flip, uint32_t width,
uint32_t height)
2013-10-01 04:37:13 +02:00
{
graphics_t graphics = thread_graphics;
float fcx, fcy;
struct vb_data *data;
assert(tex);
2014-02-24 06:39:33 +01:00
if (!tex || !thread_graphics)
return;
2013-10-01 04:37:13 +02:00
if (gs_gettexturetype(tex) != GS_TEXTURE_2D) {
blog(LOG_ERROR, "A sprite must be a 2D texture");
2013-10-01 04:37:13 +02:00
return;
}
fcx = width ? (float)width : (float)texture_getwidth(tex);
fcy = height ? (float)height : (float)texture_getheight(tex);
2013-10-01 04:37:13 +02:00
data = vertexbuffer_getdata(graphics->sprite_buffer);
if (texture_isrect(tex))
build_sprite_rect(data, tex, fcx, fcy, flip);
else
build_sprite_norm(data, fcx, fcy, flip);
vertexbuffer_flush(graphics->sprite_buffer);
2013-10-01 04:37:13 +02:00
gs_load_vertexbuffer(graphics->sprite_buffer);
gs_load_indexbuffer(NULL);
2013-10-01 04:37:13 +02:00
gs_draw(GS_TRISTRIP, 0, 0);
}
void gs_draw_cube_backdrop(texture_t cubetex, const struct quat *rot,
float left, float right, float top, float bottom, float znear)
{
/* TODO */
UNUSED_PARAMETER(cubetex);
UNUSED_PARAMETER(rot);
UNUSED_PARAMETER(left);
UNUSED_PARAMETER(right);
UNUSED_PARAMETER(top);
UNUSED_PARAMETER(bottom);
UNUSED_PARAMETER(znear);
2013-10-01 04:37:13 +02:00
}
void gs_resetviewport(void)
{
uint32_t cx, cy;
assert(thread_graphics != NULL);
2013-10-01 04:37:13 +02:00
gs_getsize(&cx, &cy);
2013-10-18 02:21:42 +02:00
gs_setviewport(0, 0, (int)cx, (int)cy);
2013-10-01 04:37:13 +02:00
}
void gs_set2dmode(void)
{
uint32_t cx, cy;
assert(thread_graphics != NULL);
2013-10-01 04:37:13 +02:00
gs_getsize(&cx, &cy);
gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -1.0, -1024.0f);
}
void gs_set3dmode(double fovy, double znear, double zvar)
{
/* TODO */
UNUSED_PARAMETER(fovy);
UNUSED_PARAMETER(znear);
UNUSED_PARAMETER(zvar);
2013-10-01 04:37:13 +02:00
}
void gs_viewport_push(void)
{
2014-02-24 06:39:33 +01:00
if (!thread_graphics) return;
2013-10-01 04:37:13 +02:00
struct gs_rect *rect = da_push_back_new(
thread_graphics->viewport_stack);
gs_getviewport(rect);
}
void gs_viewport_pop(void)
{
struct gs_rect *rect;
2014-02-24 06:39:33 +01:00
if (!thread_graphics || !thread_graphics->viewport_stack.num)
2013-10-01 04:37:13 +02:00
return;
rect = da_end(thread_graphics->viewport_stack);
gs_setviewport(rect->x, rect->y, rect->cx, rect->cy);
da_pop_back(thread_graphics->viewport_stack);
}
void texture_setimage(texture_t tex, const void *data, uint32_t linesize,
bool flip)
2013-10-01 04:37:13 +02:00
{
2014-02-24 06:39:33 +01:00
if (!thread_graphics || !tex)
return;
void *ptr;
uint32_t linesize_out;
uint32_t row_copy;
int32_t height = (int32_t)texture_getheight(tex);
int32_t y;
if (!texture_map(tex, &ptr, &linesize_out))
return;
row_copy = (linesize < linesize_out) ? linesize : linesize_out;
if (flip) {
for (y = height-1; y >= 0; y--)
memcpy((uint8_t*)ptr + (uint32_t)y * linesize_out,
(uint8_t*)data + (uint32_t)y * linesize,
row_copy);
} else if (linesize == linesize_out) {
memcpy(ptr, data, row_copy * height);
} else {
for (y = 0; y < height; y++)
memcpy((uint8_t*)ptr + (uint32_t)y * linesize_out,
(uint8_t*)data + (uint32_t)y * linesize,
row_copy);
}
texture_unmap(tex);
2013-10-01 04:37:13 +02:00
}
void cubetexture_setimage(texture_t cubetex, uint32_t side, const void *data,
uint32_t linesize, bool invert)
2013-10-01 04:37:13 +02:00
{
/* TODO */
UNUSED_PARAMETER(cubetex);
UNUSED_PARAMETER(side);
UNUSED_PARAMETER(data);
UNUSED_PARAMETER(linesize);
UNUSED_PARAMETER(invert);
2013-10-01 04:37:13 +02:00
}
void gs_perspective(float angle, float aspect, float near, float far)
{
graphics_t graphics = thread_graphics;
float xmin, xmax, ymin, ymax;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
ymax = near * tanf(RAD(angle)*0.5f);
ymin = -ymax;
xmin = ymin * aspect;
xmax = ymax * aspect;
graphics->exports.device_frustum(graphics->device, xmin, xmax,
ymin, ymax, near, far);
}
2013-10-01 04:37:13 +02:00
/* ------------------------------------------------------------------------- */
const char *gs_preprocessor_name(void)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return NULL;
return graphics->exports.device_preprocessor_name();
}
2013-10-01 04:37:13 +02:00
swapchain_t gs_create_swapchain(struct gs_init_data *data)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return NULL;
2013-10-01 04:37:13 +02:00
return graphics->exports.device_create_swapchain(graphics->device,
data);
}
void gs_resize(uint32_t x, uint32_t y)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_resize(graphics->device, x, y);
}
void gs_getsize(uint32_t *x, uint32_t *y)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_getsize(graphics->device, x, y);
}
uint32_t gs_getwidth(void)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return 0;
2013-10-01 04:37:13 +02:00
return graphics->exports.device_getwidth(graphics->device);
}
uint32_t gs_getheight(void)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return 0;
2013-10-01 04:37:13 +02:00
return graphics->exports.device_getheight(graphics->device);
}
static inline bool is_pow2(uint32_t size)
{
return size >= 2 && (size & (size-1)) == 0;
}
2013-10-01 04:37:13 +02:00
texture_t gs_create_texture(uint32_t width, uint32_t height,
enum gs_color_format color_format, uint32_t levels,
const void **data, uint32_t flags)
2013-10-01 04:37:13 +02:00
{
graphics_t graphics = thread_graphics;
bool pow2tex = is_pow2(width) && is_pow2(height);
bool uses_mipmaps = (flags & GS_BUILDMIPMAPS || levels != 1);
2014-02-24 06:39:33 +01:00
if (!graphics)
return NULL;
if (uses_mipmaps && !pow2tex) {
blog(LOG_WARNING, "Cannot use mipmaps with a "
"non-power-of-two texture. Disabling "
"mipmaps for this texture.");
uses_mipmaps = false;
flags &= ~GS_BUILDMIPMAPS;
levels = 1;
}
if (uses_mipmaps && flags & GS_RENDERTARGET) {
blog(LOG_WARNING, "Cannot use mipmaps with render targets. "
"Disabling mipmaps for this texture.");
flags &= ~GS_BUILDMIPMAPS;
levels = 1;
}
2013-10-01 04:37:13 +02:00
return graphics->exports.device_create_texture(graphics->device,
width, height, color_format, levels, data, flags);
2013-10-01 04:37:13 +02:00
}
texture_t gs_create_cubetexture(uint32_t size,
enum gs_color_format color_format, uint32_t levels,
const void **data, uint32_t flags)
2013-10-01 04:37:13 +02:00
{
graphics_t graphics = thread_graphics;
bool pow2tex = is_pow2(size);
bool uses_mipmaps = (flags & GS_BUILDMIPMAPS || levels != 1);
2014-02-24 06:39:33 +01:00
if (!graphics)
return NULL;
if (uses_mipmaps && !pow2tex) {
blog(LOG_WARNING, "Cannot use mipmaps with a "
"non-power-of-two texture. Disabling "
"mipmaps for this texture.");
uses_mipmaps = false;
flags &= ~GS_BUILDMIPMAPS;
levels = 1;
}
if (uses_mipmaps && flags & GS_RENDERTARGET) {
blog(LOG_WARNING, "Cannot use mipmaps with render targets. "
"Disabling mipmaps for this texture.");
flags &= ~GS_BUILDMIPMAPS;
levels = 1;
data = NULL;
}
2013-10-01 04:37:13 +02:00
return graphics->exports.device_create_cubetexture(graphics->device,
size, color_format, levels, data, flags);
2013-10-01 04:37:13 +02:00
}
texture_t gs_create_volumetexture(uint32_t width, uint32_t height,
uint32_t depth, enum gs_color_format color_format,
uint32_t levels, const void **data, uint32_t flags)
2013-10-01 04:37:13 +02:00
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return NULL;
2013-10-01 04:37:13 +02:00
return graphics->exports.device_create_volumetexture(graphics->device,
width, height, depth, color_format, levels, data,
flags);
2013-10-01 04:37:13 +02:00
}
zstencil_t gs_create_zstencil(uint32_t width, uint32_t height,
enum gs_zstencil_format format)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return NULL;
2013-10-01 04:37:13 +02:00
return graphics->exports.device_create_zstencil(graphics->device,
width, height, format);
}
stagesurf_t gs_create_stagesurface(uint32_t width, uint32_t height,
enum gs_color_format color_format)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return NULL;
2013-10-01 04:37:13 +02:00
return graphics->exports.device_create_stagesurface(graphics->device,
width, height, color_format);
}
samplerstate_t gs_create_samplerstate(struct gs_sampler_info *info)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return NULL;
2013-10-01 04:37:13 +02:00
return graphics->exports.device_create_samplerstate(graphics->device,
info);
}
shader_t gs_create_vertexshader(const char *shader, const char *file,
char **error_string)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return NULL;
2013-10-01 04:37:13 +02:00
return graphics->exports.device_create_vertexshader(graphics->device,
shader, file, error_string);
}
shader_t gs_create_pixelshader(const char *shader,
const char *file, char **error_string)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return NULL;
2013-10-01 04:37:13 +02:00
return graphics->exports.device_create_pixelshader(graphics->device,
shader, file, error_string);
}
vertbuffer_t gs_create_vertexbuffer(struct vb_data *data,
uint32_t flags)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return NULL;
2013-10-01 04:37:13 +02:00
return graphics->exports.device_create_vertexbuffer(graphics->device,
data, flags);
}
indexbuffer_t gs_create_indexbuffer(enum gs_index_type type,
void *indices, size_t num, uint32_t flags)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return NULL;
2013-10-01 04:37:13 +02:00
return graphics->exports.device_create_indexbuffer(graphics->device,
type, indices, num, flags);
}
enum gs_texture_type gs_gettexturetype(texture_t texture)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return GS_TEXTURE_2D;
return graphics->exports.device_gettexturetype(texture);
2013-10-01 04:37:13 +02:00
}
void gs_load_vertexbuffer(vertbuffer_t vertbuffer)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_load_vertexbuffer(graphics->device,
vertbuffer);
}
void gs_load_indexbuffer(indexbuffer_t indexbuffer)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_load_indexbuffer(graphics->device,
indexbuffer);
}
void gs_load_texture(texture_t tex, int unit)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_load_texture(graphics->device, tex, unit);
}
void gs_load_samplerstate(samplerstate_t samplerstate, int unit)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_load_samplerstate(graphics->device,
samplerstate, unit);
}
void gs_load_vertexshader(shader_t vertshader)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_load_vertexshader(graphics->device,
vertshader);
}
void gs_load_pixelshader(shader_t pixelshader)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_load_pixelshader(graphics->device,
pixelshader);
}
void gs_load_defaultsamplerstate(bool b_3d, int unit)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_load_defaultsamplerstate(graphics->device,
b_3d, unit);
}
shader_t gs_getvertexshader(void)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return NULL;
2013-10-01 04:37:13 +02:00
return graphics->exports.device_getvertexshader(graphics->device);
}
shader_t gs_getpixelshader(void)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return NULL;
2013-10-01 04:37:13 +02:00
return graphics->exports.device_getpixelshader(graphics->device);
}
texture_t gs_getrendertarget(void)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return NULL;
2013-10-01 04:37:13 +02:00
return graphics->exports.device_getrendertarget(graphics->device);
}
zstencil_t gs_getzstenciltarget(void)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return NULL;
2013-10-01 04:37:13 +02:00
return graphics->exports.device_getzstenciltarget(graphics->device);
}
void gs_setrendertarget(texture_t tex, zstencil_t zstencil)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_setrendertarget(graphics->device, tex,
zstencil);
}
void gs_setcuberendertarget(texture_t cubetex, int side, zstencil_t zstencil)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_setcuberendertarget(graphics->device, cubetex,
side, zstencil);
}
void gs_copy_texture(texture_t dst, texture_t src)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_copy_texture(graphics->device, dst, src);
}
2014-04-09 20:04:58 +02:00
void gs_copy_texture_region(texture_t dst, uint32_t dst_x, uint32_t dst_y,
texture_t src, uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h)
{
graphics_t graphics = thread_graphics;
if (!graphics) return;
graphics->exports.device_copy_texture_region(graphics->device,
dst, dst_x, dst_y,
src, src_x, src_y, src_w, src_h);
}
2013-10-01 04:37:13 +02:00
void gs_stage_texture(stagesurf_t dst, texture_t src)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_stage_texture(graphics->device, dst, src);
}
void gs_beginscene(void)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_beginscene(graphics->device);
}
void gs_draw(enum gs_draw_mode draw_mode, uint32_t start_vert,
uint32_t num_verts)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_draw(graphics->device, draw_mode,
start_vert, num_verts);
}
void gs_endscene(void)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_endscene(graphics->device);
}
void gs_load_swapchain(swapchain_t swapchain)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_load_swapchain(graphics->device, swapchain);
}
void gs_clear(uint32_t clear_flags, struct vec4 *color, float depth,
uint8_t stencil)
{
graphics_t graphics = thread_graphics;
graphics->exports.device_clear(graphics->device, clear_flags, color,
depth, stencil);
}
void gs_present(void)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_present(graphics->device);
}
void gs_flush(void)
{
graphics_t graphics = thread_graphics;
if (!graphics) return;
graphics->exports.device_flush(graphics->device);
}
2013-10-01 04:37:13 +02:00
void gs_setcullmode(enum gs_cull_mode mode)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_setcullmode(graphics->device, mode);
}
enum gs_cull_mode gs_getcullmode(void)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return GS_NEITHER;
2013-10-01 04:37:13 +02:00
return graphics->exports.device_getcullmode(graphics->device);
}
void gs_enable_blending(bool enable)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_enable_blending(graphics->device, enable);
}
void gs_enable_depthtest(bool enable)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_enable_depthtest(graphics->device, enable);
}
void gs_enable_stenciltest(bool enable)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_enable_stenciltest(graphics->device, enable);
}
void gs_enable_stencilwrite(bool enable)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_enable_stencilwrite(graphics->device, enable);
}
void gs_enable_color(bool red, bool green, bool blue, bool alpha)
2013-10-01 04:37:13 +02:00
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
graphics->exports.device_enable_color(graphics->device, red, green,
blue, alpha);
2013-10-01 04:37:13 +02:00
}
void gs_blendfunction(enum gs_blend_type src, enum gs_blend_type dest)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_blendfunction(graphics->device, src, dest);
}
void gs_depthfunction(enum gs_depth_test test)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_depthfunction(graphics->device, test);
}
void gs_stencilfunction(enum gs_stencil_side side, enum gs_depth_test test)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_stencilfunction(graphics->device, side, test);
}
void gs_stencilop(enum gs_stencil_side side, enum gs_stencil_op fail,
enum gs_stencil_op zfail, enum gs_stencil_op zpass)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_stencilop(graphics->device, side, fail, zfail,
zpass);
}
void gs_setviewport(int x, int y, int width, int height)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_setviewport(graphics->device, x, y, width,
height);
}
void gs_getviewport(struct gs_rect *rect)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_getviewport(graphics->device, rect);
}
void gs_setscissorrect(struct gs_rect *rect)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_setscissorrect(graphics->device, rect);
}
void gs_ortho(float left, float right, float top, float bottom, float znear,
float zfar)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_ortho(graphics->device, left, right, top,
bottom, znear, zfar);
}
void gs_frustum(float left, float right, float top, float bottom, float znear,
float zfar)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_frustum(graphics->device, left, right, top,
bottom, znear, zfar);
}
void gs_projection_push(void)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_projection_push(graphics->device);
}
void gs_projection_pop(void)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics) return;
2013-10-01 04:37:13 +02:00
graphics->exports.device_projection_pop(graphics->device);
}
void swapchain_destroy(swapchain_t swapchain)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !swapchain) return;
2013-10-01 04:37:13 +02:00
graphics->exports.swapchain_destroy(swapchain);
}
void shader_destroy(shader_t shader)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !shader) return;
2013-10-01 04:37:13 +02:00
graphics->exports.shader_destroy(shader);
}
int shader_numparams(shader_t shader)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !shader) return 0;
2013-10-01 04:37:13 +02:00
return graphics->exports.shader_numparams(shader);
}
2013-10-18 02:21:42 +02:00
sparam_t shader_getparambyidx(shader_t shader, uint32_t param)
2013-10-01 04:37:13 +02:00
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !shader) return NULL;
2013-10-01 04:37:13 +02:00
return graphics->exports.shader_getparambyidx(shader, param);
}
sparam_t shader_getparambyname(shader_t shader, const char *name)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !shader) return NULL;
2013-10-01 04:37:13 +02:00
return graphics->exports.shader_getparambyname(shader, name);
}
sparam_t shader_getviewprojmatrix(shader_t shader)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !shader) return NULL;
2013-10-01 04:37:13 +02:00
return graphics->exports.shader_getviewprojmatrix(shader);
}
sparam_t shader_getworldmatrix(shader_t shader)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !shader) return NULL;
2013-10-01 04:37:13 +02:00
return graphics->exports.shader_getworldmatrix(shader);
}
void shader_getparaminfo(sparam_t param, struct shader_param_info *info)
{
graphics_t graphics = thread_graphics;
if (!graphics || !param) return;
graphics->exports.shader_getparaminfo(param, info);
}
void shader_setbool(sparam_t param, bool val)
2013-10-01 04:37:13 +02:00
{
graphics_t graphics = thread_graphics;
if (!graphics || !param) return;
2014-02-24 06:39:33 +01:00
graphics->exports.shader_setbool(param, val);
2013-10-01 04:37:13 +02:00
}
void shader_setfloat(sparam_t param, float val)
2013-10-01 04:37:13 +02:00
{
graphics_t graphics = thread_graphics;
if (!graphics || !param) return;
2014-02-24 06:39:33 +01:00
graphics->exports.shader_setfloat(param, val);
2013-10-01 04:37:13 +02:00
}
void shader_setint(sparam_t param, int val)
2013-10-01 04:37:13 +02:00
{
graphics_t graphics = thread_graphics;
if (!graphics || !param) return;
2014-02-24 06:39:33 +01:00
graphics->exports.shader_setint(param, val);
2013-10-01 04:37:13 +02:00
}
void shader_setmatrix3(sparam_t param, const struct matrix3 *val)
2013-10-01 04:37:13 +02:00
{
graphics_t graphics = thread_graphics;
if (!graphics || !param) return;
2014-02-24 06:39:33 +01:00
graphics->exports.shader_setmatrix3(param, val);
2013-10-01 04:37:13 +02:00
}
void shader_setmatrix4(sparam_t param, const struct matrix4 *val)
2013-10-01 04:37:13 +02:00
{
graphics_t graphics = thread_graphics;
if (!graphics || !param) return;
2014-02-24 06:39:33 +01:00
graphics->exports.shader_setmatrix4(param, val);
2013-10-01 04:37:13 +02:00
}
void shader_setvec2(sparam_t param, const struct vec2 *val)
2013-10-01 04:37:13 +02:00
{
graphics_t graphics = thread_graphics;
if (!graphics || !param) return;
2014-02-24 06:39:33 +01:00
graphics->exports.shader_setvec2(param, val);
2013-10-01 04:37:13 +02:00
}
void shader_setvec3(sparam_t param, const struct vec3 *val)
2013-10-01 04:37:13 +02:00
{
graphics_t graphics = thread_graphics;
if (!graphics || !param) return;
2014-02-24 06:39:33 +01:00
graphics->exports.shader_setvec3(param, val);
2013-10-01 04:37:13 +02:00
}
void shader_setvec4(sparam_t param, const struct vec4 *val)
2013-10-01 04:37:13 +02:00
{
graphics_t graphics = thread_graphics;
if (!graphics || !param) return;
2014-02-24 06:39:33 +01:00
graphics->exports.shader_setvec4(param, val);
2013-10-01 04:37:13 +02:00
}
void shader_settexture(sparam_t param, texture_t val)
2013-10-01 04:37:13 +02:00
{
graphics_t graphics = thread_graphics;
if (!graphics || !param) return;
2014-02-24 06:39:33 +01:00
graphics->exports.shader_settexture(param, val);
2013-10-01 04:37:13 +02:00
}
void shader_setval(sparam_t param, const void *val, size_t size)
2013-10-01 04:37:13 +02:00
{
graphics_t graphics = thread_graphics;
if (!graphics || !param) return;
2014-02-24 06:39:33 +01:00
graphics->exports.shader_setval(param, val, size);
2013-10-01 04:37:13 +02:00
}
void shader_setdefault(sparam_t param)
2013-10-01 04:37:13 +02:00
{
graphics_t graphics = thread_graphics;
if (!graphics || !param) return;
2014-02-24 06:39:33 +01:00
graphics->exports.shader_setdefault(param);
2013-10-01 04:37:13 +02:00
}
void texture_destroy(texture_t tex)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !tex) return;
2013-10-01 04:37:13 +02:00
graphics->exports.texture_destroy(tex);
}
uint32_t texture_getwidth(texture_t tex)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !tex) return 0;
2013-10-01 04:37:13 +02:00
return graphics->exports.texture_getwidth(tex);
}
uint32_t texture_getheight(texture_t tex)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !tex) return 0;
2013-10-01 04:37:13 +02:00
return graphics->exports.texture_getheight(tex);
}
enum gs_color_format texture_getcolorformat(texture_t tex)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !tex) return GS_UNKNOWN;
2013-10-01 04:37:13 +02:00
return graphics->exports.texture_getcolorformat(tex);
}
bool texture_map(texture_t tex, void **ptr, uint32_t *linesize)
2013-10-01 04:37:13 +02:00
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !tex) return false;
return graphics->exports.texture_map(tex, ptr, linesize);
2013-10-01 04:37:13 +02:00
}
void texture_unmap(texture_t tex)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !tex) return;
2013-10-01 04:37:13 +02:00
graphics->exports.texture_unmap(tex);
}
bool texture_isrect(texture_t tex)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !tex) return false;
if (graphics->exports.texture_isrect)
return graphics->exports.texture_isrect(tex);
else
return false;
}
void *texture_getobj(texture_t tex)
{
graphics_t graphics = thread_graphics;
if (!graphics || !tex) return NULL;
return graphics->exports.texture_getobj(tex);
}
2013-10-01 04:37:13 +02:00
void cubetexture_destroy(texture_t cubetex)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !cubetex) return;
2013-10-01 04:37:13 +02:00
graphics->exports.cubetexture_destroy(cubetex);
}
uint32_t cubetexture_getsize(texture_t cubetex)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !cubetex) return 0;
2013-10-01 04:37:13 +02:00
return graphics->exports.cubetexture_getsize(cubetex);
}
enum gs_color_format cubetexture_getcolorformat(texture_t cubetex)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !cubetex) return GS_UNKNOWN;
2013-10-01 04:37:13 +02:00
return graphics->exports.cubetexture_getcolorformat(cubetex);
}
void volumetexture_destroy(texture_t voltex)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !voltex) return;
2013-10-01 04:37:13 +02:00
graphics->exports.volumetexture_destroy(voltex);
}
uint32_t volumetexture_getwidth(texture_t voltex)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !voltex) return 0;
2013-10-01 04:37:13 +02:00
return graphics->exports.volumetexture_getwidth(voltex);
}
uint32_t volumetexture_getheight(texture_t voltex)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !voltex) return 0;
2013-10-01 04:37:13 +02:00
return graphics->exports.volumetexture_getheight(voltex);
}
uint32_t volumetexture_getdepth(texture_t voltex)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !voltex) return 0;
2013-10-01 04:37:13 +02:00
return graphics->exports.volumetexture_getdepth(voltex);
}
enum gs_color_format volumetexture_getcolorformat(texture_t voltex)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !voltex) return GS_UNKNOWN;
2013-10-01 04:37:13 +02:00
return graphics->exports.volumetexture_getcolorformat(voltex);
}
void stagesurface_destroy(stagesurf_t stagesurf)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !stagesurf) return;
2013-10-01 04:37:13 +02:00
graphics->exports.stagesurface_destroy(stagesurf);
}
uint32_t stagesurface_getwidth(stagesurf_t stagesurf)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !stagesurf) return 0;
2013-10-01 04:37:13 +02:00
return graphics->exports.stagesurface_getwidth(stagesurf);
}
uint32_t stagesurface_getheight(stagesurf_t stagesurf)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !stagesurf) return 0;
2013-10-01 04:37:13 +02:00
return graphics->exports.stagesurface_getheight(stagesurf);
}
enum gs_color_format stagesurface_getcolorformat(stagesurf_t stagesurf)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !stagesurf) return GS_UNKNOWN;
2013-10-01 04:37:13 +02:00
return graphics->exports.stagesurface_getcolorformat(stagesurf);
}
Implement encoder interface (still preliminary) - Implement OBS encoder interface. It was previously incomplete, but now is reaching some level of completion, though probably should still be considered preliminary. I had originally implemented it so that encoders only have a 'reset' function to reset their parameters, but I felt that having both a 'start' and 'stop' function would be useful. Encoders are now assigned to a specific video/audio media output each rather than implicitely assigned to the main obs video/audio contexts. This allows separate encoder contexts that aren't necessarily assigned to the main video/audio context (which is useful for things such as recording specific sources). Will probably have to do this for regular obs outputs as well. When creating an encoder, you must now explicitely state whether that encoder is an audio or video encoder. Audio and video can optionally be automatically converted depending on what the encoder specifies. When something 'attaches' to an encoder, the first attachment starts the encoder, and the encoder automatically attaches to the media output context associated with it. Subsequent attachments won't have the same effect, they will just start receiving the same encoder data when the next keyframe plays (along with SEI if any). When detaching from the encoder, the last detachment will fully stop the encoder and detach the encoder from the media output context associated with the encoder. SEI must actually be exported separately; because new encoder attachments may not always be at the beginning of the stream, the first keyframe they get must have that SEI data in it. If the encoder has SEI data, it needs only add one small function to simply query that SEI data, and then that data will be handled automatically by libobs for all subsequent encoder attachments. - Implement x264 encoder plugin, move x264 files to separate plugin to separate necessary dependencies. - Change video/audio frame output structures to not use const qualifiers to prevent issues with non-const function usage elsewhere. This was an issue when writing the x264 encoder, as the x264 encoder expects non-const frame data. Change stagesurf_map to return a non-const data type to prevent this as well. - Change full range parameter of video scaler to be an enum rather than boolean
2014-03-17 00:21:34 +01:00
bool stagesurface_map(stagesurf_t stagesurf, uint8_t **data, uint32_t *linesize)
2013-10-01 04:37:13 +02:00
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !stagesurf) return false;
return graphics->exports.stagesurface_map(stagesurf, data, linesize);
2013-10-01 04:37:13 +02:00
}
void stagesurface_unmap(stagesurf_t stagesurf)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !stagesurf) return;
2013-10-01 04:37:13 +02:00
graphics->exports.stagesurface_unmap(stagesurf);
}
void zstencil_destroy(zstencil_t zstencil)
{
2014-02-24 06:39:33 +01:00
if (!thread_graphics || !zstencil) return;
2013-10-01 04:37:13 +02:00
thread_graphics->exports.zstencil_destroy(zstencil);
}
void samplerstate_destroy(samplerstate_t samplerstate)
{
2014-02-24 06:39:33 +01:00
if (!thread_graphics || !samplerstate) return;
2013-10-01 04:37:13 +02:00
thread_graphics->exports.samplerstate_destroy(samplerstate);
}
void vertexbuffer_destroy(vertbuffer_t vertbuffer)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !vertbuffer) return;
2013-10-01 04:37:13 +02:00
graphics->exports.vertexbuffer_destroy(vertbuffer);
}
void vertexbuffer_flush(vertbuffer_t vertbuffer)
2013-10-01 04:37:13 +02:00
{
2014-02-24 06:39:33 +01:00
if (!thread_graphics || !vertbuffer) return;
thread_graphics->exports.vertexbuffer_flush(vertbuffer);
2013-10-01 04:37:13 +02:00
}
struct vb_data *vertexbuffer_getdata(vertbuffer_t vertbuffer)
{
2014-02-24 06:39:33 +01:00
if (!thread_graphics || !vertbuffer) return NULL;
2013-10-01 04:37:13 +02:00
return thread_graphics->exports.vertexbuffer_getdata(vertbuffer);
}
void indexbuffer_destroy(indexbuffer_t indexbuffer)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !indexbuffer) return;
2013-10-01 04:37:13 +02:00
graphics->exports.indexbuffer_destroy(indexbuffer);
}
void indexbuffer_flush(indexbuffer_t indexbuffer)
{
2014-02-24 06:39:33 +01:00
if (!thread_graphics || !indexbuffer) return;
2013-10-01 04:37:13 +02:00
thread_graphics->exports.indexbuffer_flush(indexbuffer);
}
void *indexbuffer_getdata(indexbuffer_t indexbuffer)
{
2014-02-24 06:39:33 +01:00
if (!thread_graphics || !indexbuffer) return NULL;
2013-10-01 04:37:13 +02:00
return thread_graphics->exports.indexbuffer_getdata(indexbuffer);
}
size_t indexbuffer_numindices(indexbuffer_t indexbuffer)
{
2014-02-24 06:39:33 +01:00
if (!thread_graphics || !indexbuffer) return 0;
2013-10-01 04:37:13 +02:00
return thread_graphics->exports.indexbuffer_numindices(indexbuffer);
}
enum gs_index_type indexbuffer_gettype(indexbuffer_t indexbuffer)
{
2014-02-24 06:39:33 +01:00
if (!thread_graphics || !indexbuffer) return (enum gs_index_type)0;
2013-10-01 04:37:13 +02:00
return thread_graphics->exports.indexbuffer_gettype(indexbuffer);
}
2013-12-23 16:34:56 +01:00
#ifdef __APPLE__
2013-12-23 16:34:56 +01:00
/** Platform specific functions */
texture_t gs_create_texture_from_iosurface(void *iosurf)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !iosurf ||
!graphics->exports.texture_create_from_iosurface)
2013-12-23 16:34:56 +01:00
return NULL;
return graphics->exports.texture_create_from_iosurface(
graphics->device, iosurf);
}
bool texture_rebind_iosurface(texture_t texture, void *iosurf)
{
graphics_t graphics = thread_graphics;
2014-02-24 06:39:33 +01:00
if (!graphics || !iosurf || !graphics->exports.texture_rebind_iosurface)
2013-12-23 16:34:56 +01:00
return false;
return graphics->exports.texture_rebind_iosurface(texture, iosurf);
}
#elif _WIN32
bool gs_gdi_texture_available(void)
{
if (!thread_graphics)
return false;
return thread_graphics->exports.gdi_texture_available();
}
/** creates a windows GDI-lockable texture */
texture_t gs_create_gdi_texture(uint32_t width, uint32_t height)
{
graphics_t graphics = thread_graphics;
if (!graphics) return NULL;
if (graphics->exports.device_create_gdi_texture)
return graphics->exports.device_create_gdi_texture(
graphics->device, width, height);
return NULL;
}
void *texture_get_dc(texture_t gdi_tex)
{
if (!thread_graphics || !gdi_tex)
return NULL;
if (thread_graphics->exports.texture_get_dc)
return thread_graphics->exports.texture_get_dc(gdi_tex);
return NULL;
}
void texture_release_dc(texture_t gdi_tex)
{
if (!thread_graphics || !gdi_tex)
return;
if (thread_graphics->exports.texture_release_dc)
thread_graphics->exports.texture_release_dc(gdi_tex);
}
#endif