mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-11-13 15:27:21 +00:00
[client] egl: add portable texture upload fallback
This commit is contained in:
@@ -269,11 +269,61 @@ endif()
|
||||
add_subdirectory("${PROJECT_TOP}/resources" "${CMAKE_BINARY_DIR}/resources")
|
||||
add_subdirectory("${PROJECT_TOP}/common" "${CMAKE_BINARY_DIR}/common" )
|
||||
add_subdirectory("${PROJECT_TOP}/repos/LGMP/lgmp" "${CMAKE_BINARY_DIR}/LGMP" )
|
||||
add_subdirectory("${PROJECT_TOP}/repos/gui" "${CMAKE_BINARY_DIR}/gui" EXCLUDE_FROM_ALL)
|
||||
|
||||
if(LG_CLIENT_PLATFORM_WINDOWS AND ENABLE_EGL AND
|
||||
NOT LG_EGL_PROVIDER_TARGET)
|
||||
find_package(ANGLE REQUIRED)
|
||||
set(LG_EGL_PROVIDER_TARGET "" CACHE STRING
|
||||
"CMake target that provides EGL and OpenGL ES")
|
||||
|
||||
if(ENABLE_EGL)
|
||||
set(LG_EGL_PROVIDER_LIBRARIES)
|
||||
if(LG_EGL_PROVIDER_TARGET)
|
||||
if(NOT TARGET ${LG_EGL_PROVIDER_TARGET})
|
||||
message(FATAL_ERROR
|
||||
"LG_EGL_PROVIDER_TARGET does not name an existing CMake target: "
|
||||
"${LG_EGL_PROVIDER_TARGET}")
|
||||
endif()
|
||||
|
||||
list(APPEND LG_EGL_PROVIDER_LIBRARIES ${LG_EGL_PROVIDER_TARGET})
|
||||
elseif(LG_CLIENT_PLATFORM_WINDOWS)
|
||||
find_package(ANGLE REQUIRED)
|
||||
list(APPEND LG_EGL_PROVIDER_LIBRARIES
|
||||
ANGLE::EGL
|
||||
ANGLE::GLESv2
|
||||
)
|
||||
else()
|
||||
pkg_check_modules(RENDERER_EGL REQUIRED IMPORTED_TARGET
|
||||
egl
|
||||
gl
|
||||
)
|
||||
pkg_check_modules(RENDERER_EGL_OPT IMPORTED_TARGET
|
||||
wayland-egl
|
||||
)
|
||||
|
||||
list(APPEND LG_EGL_PROVIDER_LIBRARIES PkgConfig::RENDERER_EGL)
|
||||
if(RENDERER_EGL_OPT_FOUND)
|
||||
list(APPEND LG_EGL_PROVIDER_LIBRARIES PkgConfig::RENDERER_EGL_OPT)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(lg_egl_provider INTERFACE)
|
||||
target_link_libraries(lg_egl_provider INTERFACE
|
||||
${LG_EGL_PROVIDER_LIBRARIES}
|
||||
)
|
||||
endif()
|
||||
|
||||
add_subdirectory("${PROJECT_TOP}/repos/gui" "${CMAKE_BINARY_DIR}/gui"
|
||||
EXCLUDE_FROM_ALL)
|
||||
|
||||
target_sources(lg_gui PRIVATE
|
||||
"${PROJECT_TOP}/repos/gui/cimgui/imgui/imgui_demo.cpp"
|
||||
"${PROJECT_TOP}/repos/gui/cimplot/implot/implot_demo.cpp"
|
||||
)
|
||||
target_compile_definitions(lg_gui PRIVATE IMGUI_DISABLE_DEMO_WINDOWS)
|
||||
|
||||
if(ENABLE_EGL)
|
||||
# CMake 3.10 does not allow target_link_libraries to update a target from
|
||||
# another directory. LINK_LIBRARIES provides the equivalent dependency.
|
||||
set_property(TARGET lg_gui APPEND PROPERTY
|
||||
LINK_LIBRARIES lg_egl_provider)
|
||||
endif()
|
||||
|
||||
add_subdirectory(platform)
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(renderer_EGL LANGUAGES C CXX)
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(RENDERER_EGL REQUIRED IMPORTED_TARGET
|
||||
egl
|
||||
gl
|
||||
)
|
||||
|
||||
pkg_check_modules(RENDERER_EGL_OPT IMPORTED_TARGET
|
||||
wayland-egl
|
||||
)
|
||||
|
||||
find_program(AWK NAMES gawk mawk original-awk awk)
|
||||
|
||||
if(AWK MATCHES ".+-NOTFOUND")
|
||||
@@ -75,7 +65,7 @@ make_defines(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/shader/desktop_rgb.def.h"
|
||||
)
|
||||
|
||||
add_library(renderer_EGL STATIC
|
||||
set(RENDERER_EGL_SOURCES
|
||||
egl.c
|
||||
egldebug.c
|
||||
state.c
|
||||
@@ -84,7 +74,6 @@ add_library(renderer_EGL STATIC
|
||||
texture.c
|
||||
texture_buffer.c
|
||||
texture_framebuffer.c
|
||||
texture_dmabuf.c
|
||||
model.c
|
||||
desktop.c
|
||||
desktop_rects.c
|
||||
@@ -95,6 +84,7 @@ add_library(renderer_EGL STATIC
|
||||
framebuffer.c
|
||||
effect.c
|
||||
postprocess.c
|
||||
postprocess_order.c
|
||||
ffx.c
|
||||
filter_glsl.c
|
||||
filter_24bit.c
|
||||
@@ -106,19 +96,22 @@ add_library(renderer_EGL STATIC
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/shader/desktop_rgb.def.h"
|
||||
)
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
list(APPEND RENDERER_EGL_SOURCES texture_dmabuf.c)
|
||||
else()
|
||||
list(APPEND RENDERER_EGL_SOURCES texture_dmabuf_stub.c)
|
||||
endif()
|
||||
|
||||
add_library(renderer_EGL STATIC ${RENDERER_EGL_SOURCES})
|
||||
|
||||
target_compile_definitions(renderer_EGL PRIVATE CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1 IMGUI_IMPL_OPENGL_ES3)
|
||||
|
||||
target_link_libraries(renderer_EGL
|
||||
PkgConfig::RENDERER_EGL
|
||||
lg_egl_provider
|
||||
lg_common
|
||||
|
||||
lg_gui
|
||||
)
|
||||
if(RENDERER_EGL_OPT_FOUND)
|
||||
target_link_libraries(renderer_EGL
|
||||
PkgConfig::RENDERER_EGL_OPT
|
||||
)
|
||||
endif()
|
||||
|
||||
target_include_directories(renderer_EGL
|
||||
PRIVATE
|
||||
|
||||
@@ -424,13 +424,6 @@ bool egl_desktopUpdate(EGL_Desktop * desktop,
|
||||
|
||||
desktop->useDMA = false;
|
||||
|
||||
const char * gl_exts = (const char *)glGetString(GL_EXTENSIONS);
|
||||
if (!util_hasGLExt(gl_exts, "GL_EXT_buffer_storage"))
|
||||
{
|
||||
DEBUG_ERROR("GL_EXT_buffer_storage is needed to use EGL backend");
|
||||
return false;
|
||||
}
|
||||
|
||||
egl_textureFree(&desktop->texture);
|
||||
if (!egl_textureInit(&desktop->texture, desktop->display,
|
||||
EGL_TEXTYPE_FRAMEBUFFER))
|
||||
|
||||
@@ -38,14 +38,18 @@
|
||||
#include "cimgui.h"
|
||||
#include "cimgui_impl.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "egl_dynprocs.h"
|
||||
#include "egldebug.h"
|
||||
#include "model.h"
|
||||
#include "shader.h"
|
||||
#include "state.h"
|
||||
#include "texture.h"
|
||||
#include "texture_util.h"
|
||||
#include "damage.h"
|
||||
#include "desktop.h"
|
||||
#include "cursor.h"
|
||||
@@ -267,7 +271,8 @@ static bool egl_create(LG_Renderer ** renderer, const LG_RendererParams params,
|
||||
struct Inst * this = calloc(1, sizeof(*this));
|
||||
if (!this)
|
||||
{
|
||||
DEBUG_INFO("Failed to allocate %lu bytes", sizeof(*this));
|
||||
DEBUG_INFO("Failed to allocate %" PRIu64 " bytes",
|
||||
(uint64_t)sizeof(*this));
|
||||
return false;
|
||||
}
|
||||
*renderer = &this->base;
|
||||
@@ -776,8 +781,14 @@ static bool egl_onFrameFormat(LG_Renderer * renderer, const LG_RendererFormat fo
|
||||
/* this event runs in a second thread so we need to init it here */
|
||||
if (unlikely(!this->frameContext))
|
||||
{
|
||||
if (!eglBindAPI(EGL_OPENGL_ES_API))
|
||||
{
|
||||
DEBUG_EGL_ERROR("Failed to select the OpenGL ES API for frame uploads");
|
||||
return false;
|
||||
}
|
||||
|
||||
static EGLint attrs[] = {
|
||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||
EGL_CONTEXT_CLIENT_VERSION, 3,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
@@ -1005,6 +1016,12 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!eglBindAPI(EGL_OPENGL_ES_API))
|
||||
{
|
||||
DEBUG_EGL_ERROR("Failed to select the OpenGL ES API");
|
||||
return false;
|
||||
}
|
||||
|
||||
int maxSamples = 1;
|
||||
if (option_get_bool("egl", "multisample"))
|
||||
{
|
||||
@@ -1063,7 +1080,7 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
|
||||
EGL_CONFIG_ATTR(EGL_BUFFER_SIZE , configs[i].depth);
|
||||
if (hasFloatConfigs)
|
||||
EGL_CONFIG_ATTR(EGL_COLOR_COMPONENT_TYPE_EXT, configs[i].componentType);
|
||||
EGL_CONFIG_ATTR(EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT);
|
||||
EGL_CONFIG_ATTR(EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT);
|
||||
EGL_CONFIG_ATTR(EGL_SAMPLE_BUFFERS , maxSamples > 0 ? 1 : 0);
|
||||
EGL_CONFIG_ATTR(EGL_SAMPLES , maxSamples);
|
||||
attr[ai] = EGL_NONE;
|
||||
@@ -1125,7 +1142,7 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
|
||||
int ctxidx = 0;
|
||||
|
||||
ctxattr[ctxidx++] = EGL_CONTEXT_CLIENT_VERSION;
|
||||
ctxattr[ctxidx++] = 2;
|
||||
ctxattr[ctxidx++] = 3;
|
||||
|
||||
if (maj > 1 || (maj == 1 && min >= 5))
|
||||
{
|
||||
@@ -1166,8 +1183,27 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
|
||||
break;
|
||||
}
|
||||
|
||||
eglMakeCurrent(this->display, this->surface, this->surface, this->context);
|
||||
if (!eglMakeCurrent(this->display, this->surface, this->surface,
|
||||
this->context))
|
||||
{
|
||||
DEBUG_EGL_ERROR("Failed to make the EGL context current");
|
||||
return false;
|
||||
}
|
||||
|
||||
egl_stateInvalidate();
|
||||
egl_dynProcsInit();
|
||||
|
||||
GLint esMaj = 0;
|
||||
GLint esMin = 0;
|
||||
glGetIntegerv(GL_MAJOR_VERSION, &esMaj);
|
||||
glGetIntegerv(GL_MINOR_VERSION, &esMin);
|
||||
if (esMaj < 3)
|
||||
{
|
||||
DEBUG_ERROR("OpenGL ES 3.0 or newer is required, got %d.%d",
|
||||
esMaj, esMin);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char * gl_exts = (const char *)glGetString(GL_EXTENSIONS);
|
||||
if (!gl_exts)
|
||||
{
|
||||
@@ -1194,10 +1230,6 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
|
||||
DEBUG_INFO("GL Exts : %s", gl_exts);
|
||||
}
|
||||
|
||||
GLint esMaj, esMin;
|
||||
glGetIntegerv(GL_MAJOR_VERSION, &esMaj);
|
||||
glGetIntegerv(GL_MINOR_VERSION, &esMin);
|
||||
|
||||
if (!util_hasGLExt(gl_exts, "GL_EXT_texture_format_BGRA8888"))
|
||||
{
|
||||
DEBUG_ERROR("GL_EXT_texture_format_BGRA8888 is needed to use EGL backend");
|
||||
@@ -1229,26 +1261,17 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
|
||||
|
||||
this->scalePointer = option_get_bool("egl", "scalePointer");
|
||||
|
||||
if (!g_egl_dynProcs.glEGLImageTargetTexture2DOES)
|
||||
DEBUG_INFO("glEGLImageTargetTexture2DOES unavilable, DMA support disabled");
|
||||
else if (!g_egl_dynProcs.eglCreateImage || !g_egl_dynProcs.eglDestroyImage)
|
||||
DEBUG_INFO("eglCreateImage or eglDestroyImage unavailable, DMA support disabled");
|
||||
else if (!util_hasGLExt(client_exts, "EGL_EXT_image_dma_buf_import"))
|
||||
DEBUG_INFO("EGL_EXT_image_dma_buf_import unavailable, DMA support disabled");
|
||||
else if ((maj < 1 || (maj == 1 && min < 5)) && !util_hasGLExt(client_exts, "EGL_KHR_image_base"))
|
||||
DEBUG_INFO("Need EGL 1.5+ or EGL_KHR_image_base for eglCreateImage(KHR)");
|
||||
else
|
||||
this->dmaSupport = true;
|
||||
|
||||
this->dmaSupport = egl_texDMABUFSupported(this->display, maj, min);
|
||||
if (!this->dmaSupport)
|
||||
{
|
||||
useDMA = false;
|
||||
if (!util_hasGLExt(gl_exts, "GL_EXT_buffer_storage"))
|
||||
{
|
||||
DEBUG_ERROR("GL_EXT_buffer_storage is needed to use EGL backend");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const bool persistentUploads =
|
||||
util_hasGLExt(gl_exts, "GL_EXT_buffer_storage") &&
|
||||
g_egl_dynProcs.glBufferStorageEXT;
|
||||
egl_texUtilSetBufferMode(persistentUploads ?
|
||||
EGL_TEX_BUFFER_MODE_PERSISTENT : EGL_TEX_BUFFER_MODE_DIRECT);
|
||||
DEBUG_INFO("Texture upload method: %s",
|
||||
persistentUploads ? "persistent PBO" : "direct");
|
||||
|
||||
if (debug)
|
||||
{
|
||||
|
||||
@@ -18,14 +18,13 @@
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include "postprocess.h"
|
||||
#include "postprocess_order.h"
|
||||
#include "filters.h"
|
||||
#include "app.h"
|
||||
#include "cimgui.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
#include "common/debug.h"
|
||||
@@ -252,24 +251,6 @@ static bool savePreset(struct EGL_PostProcess * this, const char * name)
|
||||
return true;
|
||||
}
|
||||
|
||||
static int stringListIndex(StringList list, const char * str)
|
||||
{
|
||||
unsigned int count = stringlist_count(list);
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
if (strcmp(stringlist_at(list, i), str) == 0)
|
||||
return i;
|
||||
return INT_MAX;
|
||||
}
|
||||
|
||||
static int compareFilterOrder(const void * a_, const void * b_, void * opaque)
|
||||
{
|
||||
const EGL_Filter * a = *(const EGL_Filter **)a_;
|
||||
const EGL_Filter * b = *(const EGL_Filter **)b_;
|
||||
StringList order = opaque;
|
||||
|
||||
return stringListIndex(order, a->ops.id) - stringListIndex(order, b->ops.id);
|
||||
}
|
||||
|
||||
static void reorderFilters(struct EGL_PostProcess * this)
|
||||
{
|
||||
StringList order = stringlist_new(false);
|
||||
@@ -305,8 +286,9 @@ static void reorderFilters(struct EGL_PostProcess * this)
|
||||
p = end + 1;
|
||||
}
|
||||
|
||||
qsort_r(vector_data(&this->filters), vector_size(&this->filters),
|
||||
sizeof(EGL_Filter *), compareFilterOrder, order);
|
||||
EGL_Filter ** filters = vector_data(&this->filters);
|
||||
const size_t count = vector_size(&this->filters);
|
||||
egl_postProcessOrderFilters(filters, count, order);
|
||||
|
||||
stringlist_free(&order);
|
||||
free(orderStr);
|
||||
|
||||
57
client/renderers/EGL/postprocess_order.c
Normal file
57
client/renderers/EGL/postprocess_order.c
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* 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 (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, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "postprocess_order.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
static unsigned int stringListIndex(StringList list, const char * str)
|
||||
{
|
||||
const unsigned int count = stringlist_count(list);
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
if (strcmp(stringlist_at(list, i), str) == 0)
|
||||
return i;
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
static bool filterAfter(const EGL_Filter * a, const EGL_Filter * b,
|
||||
StringList order)
|
||||
{
|
||||
const unsigned int aIndex = stringListIndex(order, a->ops.id);
|
||||
const unsigned int bIndex = stringListIndex(order, b->ops.id);
|
||||
return aIndex > bIndex;
|
||||
}
|
||||
|
||||
void egl_postProcessOrderFilters(EGL_Filter ** filters, size_t count,
|
||||
StringList order)
|
||||
{
|
||||
for (size_t i = 1; i < count; ++i)
|
||||
{
|
||||
EGL_Filter * filter = filters[i];
|
||||
size_t j = i;
|
||||
while (j > 0 && filterAfter(filters[j - 1], filter, order))
|
||||
{
|
||||
filters[j] = filters[j - 1];
|
||||
--j;
|
||||
}
|
||||
filters[j] = filter;
|
||||
}
|
||||
}
|
||||
30
client/renderers/EGL/postprocess_order.h
Normal file
30
client/renderers/EGL/postprocess_order.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* 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 (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, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "filter.h"
|
||||
|
||||
#include "common/stringlist.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
void egl_postProcessOrderFilters(EGL_Filter ** filters, size_t count,
|
||||
StringList order);
|
||||
@@ -36,7 +36,6 @@
|
||||
extern const EGL_TextureOps EGL_TextureBuffer;
|
||||
extern const EGL_TextureOps EGL_TextureBufferStream;
|
||||
extern const EGL_TextureOps EGL_TextureFrameBuffer;
|
||||
extern const EGL_TextureOps EGL_TextureDMABUF;
|
||||
|
||||
bool egl_textureInit(EGL_Texture ** texture_, EGLDisplay * display,
|
||||
EGL_TexType type)
|
||||
|
||||
@@ -125,6 +125,9 @@ struct EGL_Texture
|
||||
EGL_TexFormat format;
|
||||
};
|
||||
|
||||
extern const EGL_TextureOps EGL_TextureDMABUF;
|
||||
bool egl_texDMABUFSupported(EGLDisplay display, EGLint major, EGLint minor);
|
||||
|
||||
bool egl_textureInit(EGL_Texture ** texture, EGLDisplay * display,
|
||||
EGL_TexType type);
|
||||
void egl_textureFree(EGL_Texture ** tex);
|
||||
|
||||
@@ -454,14 +454,17 @@ EGL_TexStatus egl_texBufferStreamProcess(EGL_Texture * texture,
|
||||
this->bufIndex = 0;
|
||||
buffer->updated = false;
|
||||
|
||||
/* With more than one slot the producer can fill the next PBO while this
|
||||
* upload is queued. A single-slot texture must remain locked until its
|
||||
* fence exists so the mapped buffer cannot be overwritten concurrently. */
|
||||
/* With more than one slot the producer can fill the next upload buffer
|
||||
* while this one is submitted. Keep a single-slot texture locked until the
|
||||
* upload is issued and, for persistent storage, its fence exists. */
|
||||
const bool keepLocked = this->texCount == 1;
|
||||
if (!keepLocked)
|
||||
LG_UNLOCK(this->copyLock);
|
||||
|
||||
egl_stateBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->pbo);
|
||||
const bool persistent =
|
||||
buffer->mode == EGL_TEX_BUFFER_MODE_PERSISTENT;
|
||||
egl_stateBindBuffer(GL_PIXEL_UNPACK_BUFFER,
|
||||
persistent ? buffer->pbo : 0);
|
||||
egl_stateBindTexture(0, GL_TEXTURE_2D, tex);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
@@ -475,7 +478,7 @@ EGL_TexStatus egl_texBufferStreamProcess(EGL_Texture * texture,
|
||||
texture->format.height,
|
||||
texture->format.format,
|
||||
texture->format.dataType,
|
||||
(const void *)0);
|
||||
persistent ? (const void *)0 : buffer->map);
|
||||
else
|
||||
for (int i = 0; i < damage->count; ++i)
|
||||
{
|
||||
@@ -491,11 +494,19 @@ EGL_TexStatus egl_texBufferStreamProcess(EGL_Texture * texture,
|
||||
rect->height,
|
||||
texture->format.format,
|
||||
texture->format.dataType,
|
||||
(const void *)offset);
|
||||
persistent ? (const void *)offset :
|
||||
(const uint8_t *)buffer->map + offset);
|
||||
}
|
||||
|
||||
/* Keep damage intact until all of its upload commands have been issued. */
|
||||
egl_texBufferDamageReset(damage);
|
||||
if (!persistent)
|
||||
{
|
||||
if (keepLocked)
|
||||
LG_UNLOCK(this->copyLock);
|
||||
return EGL_TEX_STATUS_UPDATED;
|
||||
}
|
||||
|
||||
this->sync[index] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
if (unlikely(!this->sync[index]))
|
||||
{
|
||||
|
||||
@@ -86,12 +86,32 @@ struct SnapshotRelease
|
||||
uint64_t handle;
|
||||
};
|
||||
|
||||
EGL_TextureOps EGL_TextureDMABUF;
|
||||
const EGL_TextureOps EGL_TextureDMABUF;
|
||||
|
||||
static bool initDone = false;
|
||||
static bool has24BitSupport = true;
|
||||
static bool hasImportModifiers = true;
|
||||
|
||||
bool egl_texDMABUFSupported(EGLDisplay display, EGLint major, EGLint minor)
|
||||
{
|
||||
const char * extensions = eglQueryString(display, EGL_EXTENSIONS);
|
||||
|
||||
if (!g_egl_dynProcs.glEGLImageTargetTexture2DOES)
|
||||
DEBUG_INFO("glEGLImageTargetTexture2DOES unavailable, DMA support disabled");
|
||||
else if (!g_egl_dynProcs.eglCreateImage || !g_egl_dynProcs.eglDestroyImage)
|
||||
DEBUG_INFO("eglCreateImage or eglDestroyImage unavailable, DMA support disabled");
|
||||
else if (!extensions ||
|
||||
!util_hasGLExt(extensions, "EGL_EXT_image_dma_buf_import"))
|
||||
DEBUG_INFO("EGL_EXT_image_dma_buf_import unavailable, DMA support disabled");
|
||||
else if ((major < 1 || (major == 1 && minor < 5)) &&
|
||||
!util_hasGLExt(extensions, "EGL_KHR_image_base"))
|
||||
DEBUG_INFO("Need EGL 1.5+ or EGL_KHR_image_base for eglCreateImage(KHR)");
|
||||
else
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// internal functions
|
||||
|
||||
static void egl_texDMABUFFree(EGL_Texture * texture);
|
||||
@@ -695,7 +715,7 @@ static void egl_texDMABUFMarkUsed(EGL_Texture * texture)
|
||||
LG_UNLOCK(parent->copyLock);
|
||||
}
|
||||
|
||||
EGL_TextureOps EGL_TextureDMABUF =
|
||||
const EGL_TextureOps EGL_TextureDMABUF =
|
||||
{
|
||||
.init = egl_texDMABUFInit,
|
||||
.free = egl_texDMABUFFree,
|
||||
|
||||
43
client/renderers/EGL/texture_dmabuf_stub.c
Normal file
43
client/renderers/EGL/texture_dmabuf_stub.c
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* 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 (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, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "texture.h"
|
||||
|
||||
static bool egl_texDMABUFInitUnsupported(EGL_Texture ** texture,
|
||||
EGL_TexType type, EGLDisplay * display)
|
||||
{
|
||||
(void)texture;
|
||||
(void)type;
|
||||
(void)display;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool egl_texDMABUFSupported(EGLDisplay display, EGLint major, EGLint minor)
|
||||
{
|
||||
(void)display;
|
||||
(void)major;
|
||||
(void)minor;
|
||||
return false;
|
||||
}
|
||||
|
||||
const EGL_TextureOps EGL_TextureDMABUF =
|
||||
{
|
||||
.init = egl_texDMABUFInitUnsupported,
|
||||
};
|
||||
@@ -29,6 +29,11 @@
|
||||
#include "egldebug.h"
|
||||
#include "egl_dynprocs.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static EGL_TexBufferMode bufferMode = EGL_TEX_BUFFER_MODE_DIRECT;
|
||||
|
||||
bool egl_texUtilGetFormat(const EGL_TexSetup * setup, EGL_TexFormat * fmt)
|
||||
{
|
||||
fmt->pixFmt = setup->pixFmt;
|
||||
@@ -37,7 +42,7 @@ bool egl_texUtilGetFormat(const EGL_TexSetup * setup, EGL_TexFormat * fmt)
|
||||
fmt->stride = setup->stride;
|
||||
fmt->pitch = setup->pitch;
|
||||
|
||||
switch(setup->pixFmt)
|
||||
switch (setup->pixFmt)
|
||||
{
|
||||
case EGL_PF_BGRA:
|
||||
fmt->bpp = 4;
|
||||
@@ -104,14 +109,33 @@ bool egl_texUtilGetFormat(const EGL_TexSetup * setup, EGL_TexFormat * fmt)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool egl_texUtilGenBuffers(const EGL_TexFormat * fmt, EGL_TexBuffer * buffers,
|
||||
int count)
|
||||
void egl_texUtilSetBufferMode(EGL_TexBufferMode mode)
|
||||
{
|
||||
for(int i = 0; i < count; ++i)
|
||||
bufferMode = mode;
|
||||
}
|
||||
|
||||
static bool egl_texUtilGenBuffersMode(const EGL_TexFormat * fmt,
|
||||
EGL_TexBuffer * buffers, int count, EGL_TexBufferMode mode)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
EGL_TexBuffer *buffer = &buffers[i];
|
||||
EGL_TexBuffer * buffer = &buffers[i];
|
||||
|
||||
buffer->size = fmt->dataSize;
|
||||
buffer->mode = mode;
|
||||
if (mode == EGL_TEX_BUFFER_MODE_DIRECT)
|
||||
{
|
||||
buffer->map = malloc(buffer->size);
|
||||
if (!buffer->map)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate %" PRIu64
|
||||
"-byte texture upload buffer", (uint64_t)buffer->size);
|
||||
return false;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
glGenBuffers(1, &buffer->pbo);
|
||||
egl_stateBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->pbo);
|
||||
g_egl_dynProcs.glBufferStorageEXT(
|
||||
@@ -130,12 +154,47 @@ bool egl_texUtilGenBuffers(const EGL_TexFormat * fmt, EGL_TexBuffer * buffers,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool egl_texUtilGenBuffers(const EGL_TexFormat * fmt, EGL_TexBuffer * buffers,
|
||||
int count)
|
||||
{
|
||||
EGL_TexBufferMode mode = bufferMode;
|
||||
if (mode == EGL_TEX_BUFFER_MODE_PERSISTENT &&
|
||||
!g_egl_dynProcs.glBufferStorageEXT)
|
||||
{
|
||||
DEBUG_WARN("Persistent texture uploads unavailable, using direct uploads");
|
||||
mode = EGL_TEX_BUFFER_MODE_DIRECT;
|
||||
}
|
||||
|
||||
if (egl_texUtilGenBuffersMode(fmt, buffers, count, mode))
|
||||
return true;
|
||||
|
||||
egl_texUtilFreeBuffers(buffers, count);
|
||||
if (mode != EGL_TEX_BUFFER_MODE_PERSISTENT)
|
||||
return false;
|
||||
|
||||
DEBUG_WARN("Persistent texture upload buffers failed, using direct uploads");
|
||||
mode = EGL_TEX_BUFFER_MODE_DIRECT;
|
||||
if (egl_texUtilGenBuffersMode(fmt, buffers, count, mode))
|
||||
return true;
|
||||
|
||||
egl_texUtilFreeBuffers(buffers, count);
|
||||
return false;
|
||||
}
|
||||
|
||||
void egl_texUtilFreeBuffers(EGL_TexBuffer * buffers, int count)
|
||||
{
|
||||
bool deleted = false;
|
||||
for(int i = 0; i < count; ++i)
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
EGL_TexBuffer *buffer = &buffers[i];
|
||||
EGL_TexBuffer * buffer = &buffers[i];
|
||||
|
||||
if (buffer->mode == EGL_TEX_BUFFER_MODE_DIRECT)
|
||||
{
|
||||
free(buffer->map);
|
||||
buffer->map = NULL;
|
||||
buffer->size = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!buffer->pbo)
|
||||
continue;
|
||||
@@ -143,7 +202,8 @@ void egl_texUtilFreeBuffers(EGL_TexBuffer * buffers, int count)
|
||||
egl_texUtilUnmapBuffer(buffer);
|
||||
glDeleteBuffers(1, &buffer->pbo);
|
||||
deleted = true;
|
||||
buffer->pbo = 0;
|
||||
buffer->pbo = 0;
|
||||
buffer->size = 0;
|
||||
}
|
||||
|
||||
if (deleted)
|
||||
@@ -152,6 +212,7 @@ void egl_texUtilFreeBuffers(EGL_TexBuffer * buffers, int count)
|
||||
|
||||
bool egl_texUtilMapBuffer(EGL_TexBuffer * buffer)
|
||||
{
|
||||
DEBUG_ASSERT(buffer->mode == EGL_TEX_BUFFER_MODE_PERSISTENT);
|
||||
egl_stateBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->pbo);
|
||||
buffer->map = glMapBufferRange(
|
||||
GL_PIXEL_UNPACK_BUFFER,
|
||||
@@ -164,7 +225,8 @@ bool egl_texUtilMapBuffer(EGL_TexBuffer * buffer)
|
||||
GL_MAP_COHERENT_BIT_EXT);
|
||||
|
||||
if (!buffer->map)
|
||||
DEBUG_GL_ERROR("glMapBufferRange failed of %lu bytes", buffer->size);
|
||||
DEBUG_GL_ERROR("glMapBufferRange failed of %" PRIu64 " bytes",
|
||||
(uint64_t)buffer->size);
|
||||
|
||||
egl_stateBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
return buffer->map;
|
||||
@@ -175,6 +237,7 @@ void egl_texUtilUnmapBuffer(EGL_TexBuffer * buffer)
|
||||
if (!buffer->map)
|
||||
return;
|
||||
|
||||
DEBUG_ASSERT(buffer->mode == EGL_TEX_BUFFER_MODE_PERSISTENT);
|
||||
egl_stateBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->pbo);
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
egl_stateBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
@@ -40,16 +40,25 @@ typedef struct EGL_TexFormat
|
||||
}
|
||||
EGL_TexFormat;
|
||||
|
||||
typedef enum EGL_TexBufferMode
|
||||
{
|
||||
EGL_TEX_BUFFER_MODE_DIRECT,
|
||||
EGL_TEX_BUFFER_MODE_PERSISTENT
|
||||
}
|
||||
EGL_TexBufferMode;
|
||||
|
||||
typedef struct EGL_TexBuffer
|
||||
{
|
||||
size_t size;
|
||||
GLuint pbo;
|
||||
void * map;
|
||||
bool updated;
|
||||
size_t size;
|
||||
GLuint pbo;
|
||||
void * map;
|
||||
bool updated;
|
||||
EGL_TexBufferMode mode;
|
||||
}
|
||||
EGL_TexBuffer;
|
||||
|
||||
bool egl_texUtilGetFormat(const EGL_TexSetup * setup, EGL_TexFormat * fmt);
|
||||
void egl_texUtilSetBufferMode(EGL_TexBufferMode mode);
|
||||
bool egl_texUtilGenBuffers(const EGL_TexFormat * fmt, EGL_TexBuffer * buffers,
|
||||
int count);
|
||||
void egl_texUtilFreeBuffers(EGL_TexBuffer * buffers, int count);
|
||||
|
||||
@@ -1007,29 +1007,81 @@ if(ENABLE_EGL)
|
||||
)
|
||||
endforeach()
|
||||
|
||||
add_executable(texture-dmabuf-tests
|
||||
texture_dmabuf_test.c
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
add_executable(texture-dmabuf-tests
|
||||
texture_dmabuf_test.c
|
||||
)
|
||||
add_dependencies(texture-dmabuf-tests renderer_EGL)
|
||||
target_include_directories(texture-dmabuf-tests PRIVATE
|
||||
"${CMAKE_BINARY_DIR}/renderers/EGL/shader"
|
||||
)
|
||||
target_link_libraries(texture-dmabuf-tests
|
||||
${EXE_FLAGS}
|
||||
lg_common
|
||||
)
|
||||
set(TEXTURE_DMABUF_CASES
|
||||
select
|
||||
poll
|
||||
saturation
|
||||
release
|
||||
identity
|
||||
)
|
||||
foreach(name IN LISTS TEXTURE_DMABUF_CASES)
|
||||
add_test(NAME texture-dmabuf-${name}
|
||||
COMMAND texture-dmabuf-tests ${name}
|
||||
)
|
||||
set_tests_properties(texture-dmabuf-${name} PROPERTIES
|
||||
TIMEOUT 10
|
||||
)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
add_executable(postprocess-order-tests
|
||||
postprocess_order_test.c
|
||||
../renderers/EGL/postprocess_order.c
|
||||
)
|
||||
add_dependencies(texture-dmabuf-tests renderer_EGL)
|
||||
target_include_directories(texture-dmabuf-tests PRIVATE
|
||||
"${CMAKE_BINARY_DIR}/renderers/EGL/shader"
|
||||
target_include_directories(postprocess-order-tests PRIVATE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../renderers/EGL"
|
||||
)
|
||||
target_link_libraries(texture-dmabuf-tests
|
||||
target_link_libraries(postprocess-order-tests
|
||||
${EXE_FLAGS}
|
||||
lg_common
|
||||
lg_egl_provider
|
||||
)
|
||||
set(TEXTURE_DMABUF_CASES
|
||||
select
|
||||
poll
|
||||
saturation
|
||||
release
|
||||
identity
|
||||
add_test(NAME postprocess-order-tests
|
||||
COMMAND postprocess-order-tests
|
||||
)
|
||||
foreach(name IN LISTS TEXTURE_DMABUF_CASES)
|
||||
add_test(NAME texture-dmabuf-${name}
|
||||
COMMAND texture-dmabuf-tests ${name}
|
||||
set_tests_properties(postprocess-order-tests PROPERTIES
|
||||
TIMEOUT 10
|
||||
)
|
||||
|
||||
add_executable(texture-upload-tests
|
||||
texture_upload_test.c
|
||||
../renderers/EGL/texture_buffer.c
|
||||
)
|
||||
target_include_directories(texture-upload-tests PRIVATE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../renderers/EGL"
|
||||
"${PROJECT_TOP}/common/include"
|
||||
)
|
||||
target_link_libraries(texture-upload-tests
|
||||
${EXE_FLAGS}
|
||||
LGProtocol::LGProtocol
|
||||
lg_egl_provider
|
||||
)
|
||||
set(TEXTURE_UPLOAD_CASES
|
||||
direct
|
||||
direct-full
|
||||
direct-rect
|
||||
persistent
|
||||
persistent-full
|
||||
persistent-rect
|
||||
fallback
|
||||
)
|
||||
foreach(name IN LISTS TEXTURE_UPLOAD_CASES)
|
||||
add_test(NAME texture-upload-${name}
|
||||
COMMAND texture-upload-tests ${name}
|
||||
)
|
||||
set_tests_properties(texture-dmabuf-${name} PROPERTIES
|
||||
set_tests_properties(texture-upload-${name} PROPERTIES
|
||||
TIMEOUT 10
|
||||
)
|
||||
endforeach()
|
||||
|
||||
73
client/tests/postprocess_order_test.c
Normal file
73
client/tests/postprocess_order_test.c
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* 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 (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, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "test.h"
|
||||
|
||||
#include "../renderers/EGL/postprocess_order.h"
|
||||
|
||||
static void checkFilterOrder(EGL_Filter ** filters, EGL_Filter ** expected,
|
||||
size_t count)
|
||||
{
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
CHECK(filters[i] == expected[i]);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
EGL_Filter first = { .ops = { .id = "first" } };
|
||||
EGL_Filter second = { .ops = { .id = "second" } };
|
||||
EGL_Filter third = { .ops = { .id = "third" } };
|
||||
EGL_Filter unknownA = { .ops = { .id = "unknown-a" } };
|
||||
EGL_Filter unknownB = { .ops = { .id = "unknown-b" } };
|
||||
|
||||
EGL_Filter * filters [] =
|
||||
{
|
||||
&unknownA,
|
||||
&second,
|
||||
&unknownB,
|
||||
&first,
|
||||
&third,
|
||||
};
|
||||
EGL_Filter * expected[] =
|
||||
{
|
||||
&first,
|
||||
&second,
|
||||
&third,
|
||||
&unknownA,
|
||||
&unknownB,
|
||||
};
|
||||
|
||||
StringList order = stringlist_new(false);
|
||||
CHECK(order);
|
||||
CHECK(stringlist_push(order, "first") == 0);
|
||||
CHECK(stringlist_push(order, "second") == 1);
|
||||
CHECK(stringlist_push(order, "third") == 2);
|
||||
|
||||
const size_t count = sizeof(filters) / sizeof(*filters);
|
||||
egl_postProcessOrderFilters(filters, count, order);
|
||||
checkFilterOrder(filters, expected, count);
|
||||
|
||||
stringlist_clear(order);
|
||||
egl_postProcessOrderFilters(filters, count, order);
|
||||
checkFilterOrder(filters, expected, count);
|
||||
|
||||
stringlist_free(&order);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
493
client/tests/texture_upload_test.c
Normal file
493
client/tests/texture_upload_test.c
Normal file
@@ -0,0 +1,493 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* 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 (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, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "test.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../renderers/EGL/texture_buffer.h"
|
||||
#include "../renderers/EGL/texture_util.c"
|
||||
|
||||
struct Trace
|
||||
{
|
||||
unsigned int nextBuffer;
|
||||
unsigned int storage;
|
||||
unsigned int map;
|
||||
unsigned int unmap;
|
||||
unsigned int deleted;
|
||||
unsigned int bufferBinds;
|
||||
unsigned int textureBinds;
|
||||
unsigned int uploads;
|
||||
unsigned int fences;
|
||||
unsigned int finishes;
|
||||
unsigned int flushes;
|
||||
GLuint boundPBO;
|
||||
GLuint boundTexture;
|
||||
GLint unpackAlignment;
|
||||
GLint unpackRowLength;
|
||||
GLint uploadX;
|
||||
GLint uploadY;
|
||||
GLsizei uploadWidth;
|
||||
GLsizei uploadHeight;
|
||||
const void * uploadData;
|
||||
bool failMap;
|
||||
};
|
||||
|
||||
static struct Trace trace;
|
||||
static uint8_t mappedStorage[4096];
|
||||
|
||||
struct EGLDynProcs g_egl_dynProcs;
|
||||
_Thread_local EGL_StateCache g_eglState;
|
||||
|
||||
void debug_level(enum DebugLevel level, const char * file, unsigned int line,
|
||||
const char * function, const char * format, ...)
|
||||
{
|
||||
}
|
||||
|
||||
static void fakeBufferStorage(GLenum target, GLsizeiptr size,
|
||||
const void * data, GLbitfield flags)
|
||||
{
|
||||
CHECK(target == GL_PIXEL_UNPACK_BUFFER);
|
||||
CHECK(size > 0);
|
||||
CHECK(!data);
|
||||
CHECK(flags & GL_MAP_PERSISTENT_BIT_EXT);
|
||||
++trace.storage;
|
||||
}
|
||||
|
||||
void glGenBuffers(GLsizei count, GLuint * buffers)
|
||||
{
|
||||
for (GLsizei i = 0; i < count; ++i)
|
||||
buffers[i] = ++trace.nextBuffer;
|
||||
}
|
||||
|
||||
void glBindBuffer(GLenum target, GLuint buffer)
|
||||
{
|
||||
if (target == GL_PIXEL_UNPACK_BUFFER)
|
||||
{
|
||||
trace.boundPBO = buffer;
|
||||
++trace.bufferBinds;
|
||||
}
|
||||
}
|
||||
|
||||
void glActiveTexture(GLenum texture)
|
||||
{
|
||||
CHECK(texture == GL_TEXTURE0);
|
||||
}
|
||||
|
||||
void glBindTexture(GLenum target, GLuint texture)
|
||||
{
|
||||
CHECK(target == GL_TEXTURE_2D);
|
||||
trace.boundTexture = texture;
|
||||
++trace.textureBinds;
|
||||
}
|
||||
|
||||
void glPixelStorei(GLenum pname, GLint param)
|
||||
{
|
||||
switch (pname)
|
||||
{
|
||||
case GL_UNPACK_ALIGNMENT:
|
||||
trace.unpackAlignment = param;
|
||||
break;
|
||||
|
||||
case GL_UNPACK_ROW_LENGTH:
|
||||
trace.unpackRowLength = param;
|
||||
break;
|
||||
|
||||
default:
|
||||
CHECK(false);
|
||||
}
|
||||
}
|
||||
|
||||
void glTexSubImage2D(GLenum target, GLint level, GLint xoffset,
|
||||
GLint yoffset, GLsizei width, GLsizei height, GLenum format,
|
||||
GLenum type, const void * pixels)
|
||||
{
|
||||
CHECK(target == GL_TEXTURE_2D);
|
||||
CHECK(level == 0);
|
||||
CHECK(format == GL_RGBA);
|
||||
CHECK(type == GL_UNSIGNED_BYTE);
|
||||
|
||||
trace.uploadX = xoffset;
|
||||
trace.uploadY = yoffset;
|
||||
trace.uploadWidth = width;
|
||||
trace.uploadHeight = height;
|
||||
trace.uploadData = pixels;
|
||||
++trace.uploads;
|
||||
}
|
||||
|
||||
GLsync glFenceSync(GLenum condition, GLbitfield flags)
|
||||
{
|
||||
CHECK(condition == GL_SYNC_GPU_COMMANDS_COMPLETE);
|
||||
CHECK(flags == 0);
|
||||
++trace.fences;
|
||||
return (GLsync)(uintptr_t)1;
|
||||
}
|
||||
|
||||
void glFinish(void)
|
||||
{
|
||||
++trace.finishes;
|
||||
}
|
||||
|
||||
void glFlush(void)
|
||||
{
|
||||
++trace.flushes;
|
||||
}
|
||||
|
||||
void * glMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length,
|
||||
GLbitfield access)
|
||||
{
|
||||
CHECK(target == GL_PIXEL_UNPACK_BUFFER);
|
||||
CHECK(offset == 0);
|
||||
CHECK(length <= (GLsizeiptr)sizeof(mappedStorage));
|
||||
CHECK(access & GL_MAP_PERSISTENT_BIT_EXT);
|
||||
++trace.map;
|
||||
return trace.failMap ? NULL : mappedStorage;
|
||||
}
|
||||
|
||||
GLboolean glUnmapBuffer(GLenum target)
|
||||
{
|
||||
CHECK(target == GL_PIXEL_UNPACK_BUFFER);
|
||||
++trace.unmap;
|
||||
return GL_TRUE;
|
||||
}
|
||||
|
||||
void glDeleteBuffers(GLsizei count, const GLuint * buffers)
|
||||
{
|
||||
trace.deleted += count;
|
||||
}
|
||||
|
||||
void egl_stateInvalidateShared(void)
|
||||
{
|
||||
memset(&g_eglState, 0, sizeof(g_eglState));
|
||||
}
|
||||
|
||||
const char * gl_getErrorStr(void)
|
||||
{
|
||||
return "fake";
|
||||
}
|
||||
|
||||
static void reset(void)
|
||||
{
|
||||
memset(&trace, 0, sizeof(trace));
|
||||
memset(&g_eglState, 0, sizeof(g_eglState));
|
||||
memset(&g_egl_dynProcs, 0, sizeof(g_egl_dynProcs));
|
||||
g_egl_dynProcs.glBufferStorageEXT = fakeBufferStorage;
|
||||
}
|
||||
|
||||
static EGL_TexFormat testFormat(void)
|
||||
{
|
||||
return (EGL_TexFormat)
|
||||
{
|
||||
.dataSize = 1024,
|
||||
};
|
||||
}
|
||||
|
||||
static void initTexture(TextureBuffer * texture, EGL_TexBufferMode mode)
|
||||
{
|
||||
memset(texture, 0, sizeof(*texture));
|
||||
|
||||
texture->base.format.width = 8;
|
||||
texture->base.format.height = 4;
|
||||
texture->base.format.stride = 10;
|
||||
texture->base.format.pitch = 40;
|
||||
texture->base.format.bpp = 4;
|
||||
texture->base.format.format = GL_RGBA;
|
||||
texture->base.format.dataType = GL_UNSIGNED_BYTE;
|
||||
texture->base.frameToken = LG_RENDERER_FRAME_TOKEN_NONE;
|
||||
texture->texCount = 1;
|
||||
texture->tex[0] = 17;
|
||||
texture->buf[0].mode = mode;
|
||||
texture->buf[0].pbo =
|
||||
mode == EGL_TEX_BUFFER_MODE_PERSISTENT ? 23 : 0;
|
||||
texture->buf[0].map = mappedStorage;
|
||||
texture->buf[0].size = sizeof(mappedStorage);
|
||||
texture->buf[0].updated = true;
|
||||
texture->slotToken[0] = 42;
|
||||
texture->rIndex = -1;
|
||||
LG_LOCK_INIT(texture->copyLock);
|
||||
}
|
||||
|
||||
static void checkLockReleased(TextureBuffer * texture)
|
||||
{
|
||||
const bool locked = atomic_flag_test_and_set_explicit(
|
||||
&texture->copyLock, memory_order_acquire);
|
||||
CHECK(!locked);
|
||||
atomic_flag_clear_explicit(&texture->copyLock, memory_order_release);
|
||||
}
|
||||
|
||||
static void testDirect(void)
|
||||
{
|
||||
reset();
|
||||
egl_texUtilSetBufferMode(EGL_TEX_BUFFER_MODE_DIRECT);
|
||||
|
||||
EGL_TexBuffer buffers[2] = { 0 };
|
||||
const EGL_TexFormat format = testFormat();
|
||||
CHECK(egl_texUtilGenBuffers(&format, buffers, 2));
|
||||
CHECK(buffers[0].mode == EGL_TEX_BUFFER_MODE_DIRECT);
|
||||
CHECK(buffers[1].mode == EGL_TEX_BUFFER_MODE_DIRECT);
|
||||
CHECK(buffers[0].map);
|
||||
CHECK(buffers[1].map);
|
||||
CHECK(!buffers[0].pbo);
|
||||
CHECK(!buffers[1].pbo);
|
||||
CHECK(trace.storage == 0);
|
||||
CHECK(trace.map == 0);
|
||||
|
||||
egl_texUtilFreeBuffers(buffers, 2);
|
||||
CHECK(!buffers[0].map);
|
||||
CHECK(!buffers[1].map);
|
||||
CHECK(trace.deleted == 0);
|
||||
}
|
||||
|
||||
static void testPersistent(void)
|
||||
{
|
||||
reset();
|
||||
egl_texUtilSetBufferMode(EGL_TEX_BUFFER_MODE_PERSISTENT);
|
||||
|
||||
EGL_TexBuffer buffer = { 0 };
|
||||
const EGL_TexFormat format = testFormat();
|
||||
CHECK(egl_texUtilGenBuffers(&format, &buffer, 1));
|
||||
CHECK(buffer.mode == EGL_TEX_BUFFER_MODE_PERSISTENT);
|
||||
CHECK(buffer.map == mappedStorage);
|
||||
CHECK(buffer.pbo == 1);
|
||||
CHECK(trace.storage == 1);
|
||||
CHECK(trace.map == 1);
|
||||
|
||||
egl_texUtilFreeBuffers(&buffer, 1);
|
||||
CHECK(!buffer.map);
|
||||
CHECK(!buffer.pbo);
|
||||
CHECK(trace.unmap == 1);
|
||||
CHECK(trace.deleted == 1);
|
||||
}
|
||||
|
||||
static void testFallback(void)
|
||||
{
|
||||
reset();
|
||||
trace.failMap = true;
|
||||
egl_texUtilSetBufferMode(EGL_TEX_BUFFER_MODE_PERSISTENT);
|
||||
|
||||
EGL_TexBuffer buffer = { 0 };
|
||||
const EGL_TexFormat format = testFormat();
|
||||
CHECK(egl_texUtilGenBuffers(&format, &buffer, 1));
|
||||
CHECK(buffer.mode == EGL_TEX_BUFFER_MODE_DIRECT);
|
||||
CHECK(buffer.map);
|
||||
CHECK(!buffer.pbo);
|
||||
CHECK(trace.storage == 1);
|
||||
CHECK(trace.map == 1);
|
||||
CHECK(trace.unmap == 0);
|
||||
CHECK(trace.deleted == 1);
|
||||
|
||||
egl_texUtilFreeBuffers(&buffer, 1);
|
||||
CHECK(!buffer.map);
|
||||
|
||||
reset();
|
||||
EGL_TexBuffer next = { 0 };
|
||||
CHECK(egl_texUtilGenBuffers(&format, &next, 1));
|
||||
CHECK(next.mode == EGL_TEX_BUFFER_MODE_PERSISTENT);
|
||||
CHECK(next.map == mappedStorage);
|
||||
CHECK(next.pbo == 1);
|
||||
egl_texUtilFreeBuffers(&next, 1);
|
||||
}
|
||||
|
||||
static void testDirectFull(void)
|
||||
{
|
||||
reset();
|
||||
|
||||
TextureBuffer texture;
|
||||
initTexture(&texture, EGL_TEX_BUFFER_MODE_DIRECT);
|
||||
texture.damage[0].full = true;
|
||||
|
||||
CHECK(egl_texBufferStreamProcess(&texture.base, 42) ==
|
||||
EGL_TEX_STATUS_UPDATED);
|
||||
CHECK(trace.bufferBinds == 1);
|
||||
CHECK(trace.boundPBO == 0);
|
||||
CHECK(trace.textureBinds == 1);
|
||||
CHECK(trace.boundTexture == texture.tex[0]);
|
||||
CHECK(trace.unpackAlignment == 1);
|
||||
CHECK(trace.unpackRowLength == (GLint)texture.base.format.stride);
|
||||
CHECK(trace.uploads == 1);
|
||||
CHECK(trace.uploadX == 0);
|
||||
CHECK(trace.uploadY == 0);
|
||||
CHECK(trace.uploadWidth == (GLsizei)texture.base.format.width);
|
||||
CHECK(trace.uploadHeight == (GLsizei)texture.base.format.height);
|
||||
CHECK(trace.uploadData == mappedStorage);
|
||||
CHECK(trace.fences == 0);
|
||||
CHECK(trace.finishes == 0);
|
||||
CHECK(trace.flushes == 0);
|
||||
CHECK(!texture.buf[0].updated);
|
||||
CHECK(!texture.damage[0].full);
|
||||
CHECK(texture.damage[0].count == 0);
|
||||
CHECK(texture.rIndex == 0);
|
||||
CHECK(texture.bufIndex == 0);
|
||||
CHECK(texture.base.frameToken == 42);
|
||||
checkLockReleased(&texture);
|
||||
}
|
||||
|
||||
static void testDirectRect(void)
|
||||
{
|
||||
reset();
|
||||
|
||||
TextureBuffer texture;
|
||||
initTexture(&texture, EGL_TEX_BUFFER_MODE_DIRECT);
|
||||
texture.damage[0].count = 1;
|
||||
texture.damage[0].rects[0].x = 2;
|
||||
texture.damage[0].rects[0].y = 1;
|
||||
texture.damage[0].rects[0].width = 3;
|
||||
texture.damage[0].rects[0].height = 2;
|
||||
|
||||
const size_t offset =
|
||||
texture.damage[0].rects[0].y * texture.base.format.pitch +
|
||||
texture.damage[0].rects[0].x * texture.base.format.bpp;
|
||||
|
||||
CHECK(egl_texBufferStreamProcess(&texture.base, 42) ==
|
||||
EGL_TEX_STATUS_UPDATED);
|
||||
CHECK(trace.bufferBinds == 1);
|
||||
CHECK(trace.boundPBO == 0);
|
||||
CHECK(trace.textureBinds == 1);
|
||||
CHECK(trace.boundTexture == texture.tex[0]);
|
||||
CHECK(trace.unpackAlignment == 1);
|
||||
CHECK(trace.unpackRowLength == (GLint)texture.base.format.stride);
|
||||
CHECK(trace.uploads == 1);
|
||||
CHECK(trace.uploadX == 2);
|
||||
CHECK(trace.uploadY == 1);
|
||||
CHECK(trace.uploadWidth == 3);
|
||||
CHECK(trace.uploadHeight == 2);
|
||||
CHECK(trace.uploadData == mappedStorage + offset);
|
||||
CHECK(trace.fences == 0);
|
||||
CHECK(trace.finishes == 0);
|
||||
CHECK(trace.flushes == 0);
|
||||
CHECK(!texture.buf[0].updated);
|
||||
CHECK(!texture.damage[0].full);
|
||||
CHECK(texture.damage[0].count == 0);
|
||||
CHECK(texture.rIndex == 0);
|
||||
CHECK(texture.bufIndex == 0);
|
||||
CHECK(texture.base.frameToken == 42);
|
||||
checkLockReleased(&texture);
|
||||
}
|
||||
|
||||
static void testPersistentFull(void)
|
||||
{
|
||||
reset();
|
||||
|
||||
TextureBuffer texture;
|
||||
initTexture(&texture, EGL_TEX_BUFFER_MODE_PERSISTENT);
|
||||
texture.damage[0].full = true;
|
||||
|
||||
CHECK(egl_texBufferStreamProcess(&texture.base, 42) ==
|
||||
EGL_TEX_STATUS_UPDATED);
|
||||
CHECK(trace.bufferBinds == 1);
|
||||
CHECK(trace.boundPBO == texture.buf[0].pbo);
|
||||
CHECK(trace.textureBinds == 1);
|
||||
CHECK(trace.boundTexture == texture.tex[0]);
|
||||
CHECK(trace.unpackAlignment == 1);
|
||||
CHECK(trace.unpackRowLength == (GLint)texture.base.format.stride);
|
||||
CHECK(trace.uploads == 1);
|
||||
CHECK(trace.uploadX == 0);
|
||||
CHECK(trace.uploadY == 0);
|
||||
CHECK(trace.uploadWidth == (GLsizei)texture.base.format.width);
|
||||
CHECK(trace.uploadHeight == (GLsizei)texture.base.format.height);
|
||||
CHECK(!trace.uploadData);
|
||||
CHECK(trace.fences == 1);
|
||||
CHECK(trace.finishes == 0);
|
||||
CHECK(trace.flushes == 1);
|
||||
CHECK(texture.sync[0] == (GLsync)(uintptr_t)1);
|
||||
CHECK(!texture.buf[0].updated);
|
||||
CHECK(!texture.damage[0].full);
|
||||
CHECK(texture.damage[0].count == 0);
|
||||
CHECK(texture.rIndex == 0);
|
||||
CHECK(texture.bufIndex == 0);
|
||||
CHECK(texture.base.frameToken == 42);
|
||||
checkLockReleased(&texture);
|
||||
}
|
||||
|
||||
static void testPersistentRect(void)
|
||||
{
|
||||
reset();
|
||||
|
||||
TextureBuffer texture;
|
||||
initTexture(&texture, EGL_TEX_BUFFER_MODE_PERSISTENT);
|
||||
texture.damage[0].count = 1;
|
||||
texture.damage[0].rects[0].x = 2;
|
||||
texture.damage[0].rects[0].y = 1;
|
||||
texture.damage[0].rects[0].width = 3;
|
||||
texture.damage[0].rects[0].height = 2;
|
||||
|
||||
const uintptr_t offset =
|
||||
texture.damage[0].rects[0].y * texture.base.format.pitch +
|
||||
texture.damage[0].rects[0].x * texture.base.format.bpp;
|
||||
|
||||
CHECK(egl_texBufferStreamProcess(&texture.base, 42) ==
|
||||
EGL_TEX_STATUS_UPDATED);
|
||||
CHECK(trace.bufferBinds == 1);
|
||||
CHECK(trace.boundPBO == texture.buf[0].pbo);
|
||||
CHECK(trace.textureBinds == 1);
|
||||
CHECK(trace.boundTexture == texture.tex[0]);
|
||||
CHECK(trace.unpackAlignment == 1);
|
||||
CHECK(trace.unpackRowLength == (GLint)texture.base.format.stride);
|
||||
CHECK(trace.uploads == 1);
|
||||
CHECK(trace.uploadX == 2);
|
||||
CHECK(trace.uploadY == 1);
|
||||
CHECK(trace.uploadWidth == 3);
|
||||
CHECK(trace.uploadHeight == 2);
|
||||
CHECK(trace.uploadData == (const void *)offset);
|
||||
CHECK(trace.fences == 1);
|
||||
CHECK(trace.finishes == 0);
|
||||
CHECK(trace.flushes == 1);
|
||||
CHECK(texture.sync[0] == (GLsync)(uintptr_t)1);
|
||||
CHECK(!texture.buf[0].updated);
|
||||
CHECK(!texture.damage[0].full);
|
||||
CHECK(texture.damage[0].count == 0);
|
||||
CHECK(texture.rIndex == 0);
|
||||
CHECK(texture.bufIndex == 0);
|
||||
CHECK(texture.base.frameToken == 42);
|
||||
checkLockReleased(&texture);
|
||||
}
|
||||
|
||||
struct Test
|
||||
{
|
||||
const char * name;
|
||||
void (*run)(void);
|
||||
};
|
||||
|
||||
static const struct Test tests[] =
|
||||
{
|
||||
{ "direct" , testDirect },
|
||||
{ "persistent" , testPersistent },
|
||||
{ "fallback" , testFallback },
|
||||
{ "direct-full" , testDirectFull },
|
||||
{ "direct-rect" , testDirectRect },
|
||||
{ "persistent-full" , testPersistentFull },
|
||||
{ "persistent-rect" , testPersistentRect },
|
||||
};
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
CHECK(argc == 2);
|
||||
for (unsigned int i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i)
|
||||
if (!strcmp(argv[1], tests[i].name))
|
||||
{
|
||||
tests[i].run();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
Reference in New Issue
Block a user