This required bringing back osmesa code from 25.0.7 which was the last version where it was supported. Seems to work fine though
1598 lines
49 KiB
Diff
1598 lines
49 KiB
Diff
diff -ruN mesa-26.1.4/include/GL/osmesa.h mesa-26.1.4-banan_os/include/GL/osmesa.h
|
|
--- mesa-26.1.4/include/GL/osmesa.h 1970-01-01 02:00:00.000000000 +0200
|
|
+++ mesa-26.1.4-banan_os/include/GL/osmesa.h 2026-07-05 04:20:13.927530490 +0300
|
|
@@ -0,0 +1,332 @@
|
|
+/*
|
|
+ * Mesa 3-D graphics library
|
|
+ *
|
|
+ * Copyright (C) 1999-2005 Brian Paul All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be included
|
|
+ * in all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
+ * OTHER DEALINGS IN THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+
|
|
+/*
|
|
+ * Mesa Off-Screen rendering interface.
|
|
+ *
|
|
+ * This is an operating system and window system independent interface to
|
|
+ * Mesa which allows one to render images into a client-supplied buffer in
|
|
+ * main memory. Such images may manipulated or saved in whatever way the
|
|
+ * client wants.
|
|
+ *
|
|
+ * These are the API functions:
|
|
+ * OSMesaCreateContext - create a new Off-Screen Mesa rendering context
|
|
+ * OSMesaMakeCurrent - bind an OSMesaContext to a client's image buffer
|
|
+ * and make the specified context the current one.
|
|
+ * OSMesaDestroyContext - destroy an OSMesaContext
|
|
+ * OSMesaGetCurrentContext - return thread's current context ID
|
|
+ * OSMesaPixelStore - controls how pixels are stored in image buffer
|
|
+ * OSMesaGetIntegerv - return OSMesa state parameters
|
|
+ *
|
|
+ *
|
|
+ * The limits on the width and height of an image buffer can be retrieved
|
|
+ * via OSMesaGetIntegerv(OSMESA_MAX_WIDTH/OSMESA_MAX_HEIGHT).
|
|
+ */
|
|
+
|
|
+
|
|
+#ifndef OSMESA_H
|
|
+#define OSMESA_H
|
|
+
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+
|
|
+#include <GL/gl.h>
|
|
+
|
|
+
|
|
+#define OSMESA_MAJOR_VERSION 11
|
|
+#define OSMESA_MINOR_VERSION 2
|
|
+#define OSMESA_PATCH_VERSION 0
|
|
+
|
|
+
|
|
+
|
|
+/*
|
|
+ * Values for the format parameter of OSMesaCreateContext()
|
|
+ * New in version 2.0.
|
|
+ */
|
|
+#define OSMESA_COLOR_INDEX GL_COLOR_INDEX
|
|
+#define OSMESA_RGBA GL_RGBA
|
|
+#define OSMESA_BGRA 0x1
|
|
+#define OSMESA_ARGB 0x2
|
|
+#define OSMESA_RGB GL_RGB
|
|
+#define OSMESA_BGR 0x4
|
|
+#define OSMESA_RGB_565 0x5
|
|
+
|
|
+
|
|
+/*
|
|
+ * OSMesaPixelStore() parameters:
|
|
+ * New in version 2.0.
|
|
+ */
|
|
+#define OSMESA_ROW_LENGTH 0x10
|
|
+#define OSMESA_Y_UP 0x11
|
|
+
|
|
+
|
|
+/*
|
|
+ * Accepted by OSMesaGetIntegerv:
|
|
+ */
|
|
+#define OSMESA_WIDTH 0x20
|
|
+#define OSMESA_HEIGHT 0x21
|
|
+#define OSMESA_FORMAT 0x22
|
|
+#define OSMESA_TYPE 0x23
|
|
+#define OSMESA_MAX_WIDTH 0x24 /* new in 4.0 */
|
|
+#define OSMESA_MAX_HEIGHT 0x25 /* new in 4.0 */
|
|
+
|
|
+/*
|
|
+ * Accepted in OSMesaCreateContextAttrib's attribute list.
|
|
+ */
|
|
+#define OSMESA_DEPTH_BITS 0x30
|
|
+#define OSMESA_STENCIL_BITS 0x31
|
|
+#define OSMESA_ACCUM_BITS 0x32
|
|
+#define OSMESA_PROFILE 0x33
|
|
+#define OSMESA_CORE_PROFILE 0x34
|
|
+#define OSMESA_COMPAT_PROFILE 0x35
|
|
+#define OSMESA_CONTEXT_MAJOR_VERSION 0x36
|
|
+#define OSMESA_CONTEXT_MINOR_VERSION 0x37
|
|
+
|
|
+
|
|
+typedef struct osmesa_context *OSMesaContext;
|
|
+
|
|
+
|
|
+/*
|
|
+ * Create an Off-Screen Mesa rendering context. The only attribute needed is
|
|
+ * an RGBA vs Color-Index mode flag.
|
|
+ *
|
|
+ * Input: format - one of OSMESA_COLOR_INDEX, OSMESA_RGBA, OSMESA_BGRA,
|
|
+ * OSMESA_ARGB, OSMESA_RGB, or OSMESA_BGR.
|
|
+ * sharelist - specifies another OSMesaContext with which to share
|
|
+ * display lists. NULL indicates no sharing.
|
|
+ * Return: an OSMesaContext or 0 if error
|
|
+ */
|
|
+GLAPI OSMesaContext APIENTRY
|
|
+OSMesaCreateContext( GLenum format, OSMesaContext sharelist );
|
|
+
|
|
+
|
|
+
|
|
+/*
|
|
+ * Create an Off-Screen Mesa rendering context and specify desired
|
|
+ * size of depth buffer, stencil buffer and accumulation buffer.
|
|
+ * If you specify zero for depthBits, stencilBits, accumBits you
|
|
+ * can save some memory.
|
|
+ *
|
|
+ * New in Mesa 3.5
|
|
+ */
|
|
+GLAPI OSMesaContext APIENTRY
|
|
+OSMesaCreateContextExt( GLenum format, GLint depthBits, GLint stencilBits,
|
|
+ GLint accumBits, OSMesaContext sharelist);
|
|
+
|
|
+
|
|
+/*
|
|
+ * Create an Off-Screen Mesa rendering context with attribute list.
|
|
+ * The list is composed of (attribute, value) pairs and terminated with
|
|
+ * attribute==0. Supported Attributes:
|
|
+ *
|
|
+ * Attributes Values
|
|
+ * --------------------------------------------------------------------------
|
|
+ * OSMESA_FORMAT OSMESA_RGBA*, OSMESA_BGRA, OSMESA_ARGB, etc.
|
|
+ * OSMESA_DEPTH_BITS 0*, 16, 24, 32
|
|
+ * OSMESA_STENCIL_BITS 0*, 8
|
|
+ * OSMESA_ACCUM_BITS 0*, 16
|
|
+ * OSMESA_PROFILE OSMESA_COMPAT_PROFILE*, OSMESA_CORE_PROFILE
|
|
+ * OSMESA_CONTEXT_MAJOR_VERSION 1*, 2, 3
|
|
+ * OSMESA_CONTEXT_MINOR_VERSION 0+
|
|
+ *
|
|
+ * Note: * = default value
|
|
+ *
|
|
+ * We return a context version >= what's specified by OSMESA_CONTEXT_MAJOR/
|
|
+ * MINOR_VERSION for the given profile. For example, if you request a GL 1.4
|
|
+ * compat profile, you might get a GL 3.0 compat profile.
|
|
+ * Otherwise, null is returned if the version/profile is not supported.
|
|
+ *
|
|
+ * New in Mesa 11.2
|
|
+ */
|
|
+GLAPI OSMesaContext APIENTRY
|
|
+OSMesaCreateContextAttribs( const int *attribList, OSMesaContext sharelist );
|
|
+
|
|
+
|
|
+
|
|
+/*
|
|
+ * Destroy an Off-Screen Mesa rendering context.
|
|
+ *
|
|
+ * Input: ctx - the context to destroy
|
|
+ */
|
|
+GLAPI void APIENTRY
|
|
+OSMesaDestroyContext( OSMesaContext ctx );
|
|
+
|
|
+
|
|
+
|
|
+/*
|
|
+ * Bind an OSMesaContext to an image buffer. The image buffer is just a
|
|
+ * block of memory which the client provides. Its size must be at least
|
|
+ * as large as width*height*sizeof(type). Its address should be a multiple
|
|
+ * of 4 if using RGBA mode.
|
|
+ *
|
|
+ * Image data is stored in the order of glDrawPixels: row-major order
|
|
+ * with the lower-left image pixel stored in the first array position
|
|
+ * (ie. bottom-to-top).
|
|
+ *
|
|
+ * Since the only type initially supported is GL_UNSIGNED_BYTE, if the
|
|
+ * context is in RGBA mode, each pixel will be stored as a 4-byte RGBA
|
|
+ * value. If the context is in color indexed mode, each pixel will be
|
|
+ * stored as a 1-byte value.
|
|
+ *
|
|
+ * If the context's viewport hasn't been initialized yet, it will now be
|
|
+ * initialized to (0,0,width,height).
|
|
+ *
|
|
+ * Input: ctx - the rendering context
|
|
+ * buffer - the image buffer memory
|
|
+ * type - data type for pixel components, only GL_UNSIGNED_BYTE
|
|
+ * supported now
|
|
+ * width, height - size of image buffer in pixels, at least 1
|
|
+ * Return: GL_TRUE if success, GL_FALSE if error because of invalid ctx,
|
|
+ * invalid buffer address, type!=GL_UNSIGNED_BYTE, width<1, height<1,
|
|
+ * width>internal limit or height>internal limit.
|
|
+ */
|
|
+GLAPI GLboolean APIENTRY
|
|
+OSMesaMakeCurrent( OSMesaContext ctx, void *buffer, GLenum type,
|
|
+ GLsizei width, GLsizei height );
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+/*
|
|
+ * Return the current Off-Screen Mesa rendering context handle.
|
|
+ */
|
|
+GLAPI OSMesaContext APIENTRY
|
|
+OSMesaGetCurrentContext( void );
|
|
+
|
|
+
|
|
+
|
|
+/*
|
|
+ * Set pixel store/packing parameters for the current context.
|
|
+ * This is similar to glPixelStore.
|
|
+ * Input: pname - OSMESA_ROW_LENGTH
|
|
+ * specify actual pixels per row in image buffer
|
|
+ * 0 = same as image width (default)
|
|
+ * OSMESA_Y_UP
|
|
+ * zero = Y coordinates increase downward
|
|
+ * non-zero = Y coordinates increase upward (default)
|
|
+ * value - the value for the parameter pname
|
|
+ *
|
|
+ * New in version 2.0.
|
|
+ */
|
|
+GLAPI void APIENTRY
|
|
+OSMesaPixelStore( GLint pname, GLint value );
|
|
+
|
|
+
|
|
+
|
|
+/*
|
|
+ * Return an integer value like glGetIntegerv.
|
|
+ * Input: pname -
|
|
+ * OSMESA_WIDTH return current image width
|
|
+ * OSMESA_HEIGHT return current image height
|
|
+ * OSMESA_FORMAT return image format
|
|
+ * OSMESA_TYPE return color component data type
|
|
+ * OSMESA_ROW_LENGTH return row length in pixels
|
|
+ * OSMESA_Y_UP returns 1 or 0 to indicate Y axis direction
|
|
+ * value - pointer to integer in which to return result.
|
|
+ */
|
|
+GLAPI void APIENTRY
|
|
+OSMesaGetIntegerv( GLint pname, GLint *value );
|
|
+
|
|
+
|
|
+
|
|
+/*
|
|
+ * Return the depth buffer associated with an OSMesa context.
|
|
+ * Input: c - the OSMesa context
|
|
+ * Output: width, height - size of buffer in pixels
|
|
+ * bytesPerValue - bytes per depth value (2 or 4)
|
|
+ * buffer - pointer to depth buffer values
|
|
+ * Return: GL_TRUE or GL_FALSE to indicate success or failure.
|
|
+ *
|
|
+ * New in Mesa 2.4.
|
|
+ */
|
|
+GLAPI GLboolean APIENTRY
|
|
+OSMesaGetDepthBuffer( OSMesaContext c, GLint *width, GLint *height,
|
|
+ GLint *bytesPerValue, void **buffer );
|
|
+
|
|
+
|
|
+
|
|
+/*
|
|
+ * Return the color buffer associated with an OSMesa context.
|
|
+ * Input: c - the OSMesa context
|
|
+ * Output: width, height - size of buffer in pixels
|
|
+ * format - buffer format (OSMESA_FORMAT)
|
|
+ * buffer - pointer to depth buffer values
|
|
+ * Return: GL_TRUE or GL_FALSE to indicate success or failure.
|
|
+ *
|
|
+ * New in Mesa 3.3.
|
|
+ */
|
|
+GLAPI GLboolean APIENTRY
|
|
+OSMesaGetColorBuffer( OSMesaContext c, GLint *width, GLint *height,
|
|
+ GLint *format, void **buffer );
|
|
+
|
|
+
|
|
+
|
|
+/**
|
|
+ * This typedef is new in Mesa 6.3.
|
|
+ */
|
|
+typedef void (*OSMESAproc)();
|
|
+
|
|
+
|
|
+/*
|
|
+ * Return pointer to the named function.
|
|
+ * New in Mesa 4.1
|
|
+ * Return OSMESAproc in 6.3.
|
|
+ */
|
|
+GLAPI OSMESAproc APIENTRY
|
|
+OSMesaGetProcAddress( const char *funcName );
|
|
+
|
|
+
|
|
+
|
|
+/**
|
|
+ * Enable/disable color clamping, off by default.
|
|
+ * New in Mesa 6.4.2
|
|
+ */
|
|
+GLAPI void APIENTRY
|
|
+OSMesaColorClamp(GLboolean enable);
|
|
+
|
|
+
|
|
+/**
|
|
+ * Enable/disable Gallium post-process filters.
|
|
+ * This should be called after a context is created, but before it is
|
|
+ * made current for the first time. After a context has been made
|
|
+ * current, this function has no effect.
|
|
+ * If the enable_value param is zero, the filter is disabled. Otherwise
|
|
+ * the filter is enabled, and the value may control the filter's quality.
|
|
+ * New in Mesa 10.0
|
|
+ */
|
|
+GLAPI void APIENTRY
|
|
+OSMesaPostprocess(OSMesaContext osmesa, const char *filter,
|
|
+ unsigned enable_value);
|
|
+
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+
|
|
+
|
|
+#endif
|
|
diff -ruN mesa-26.1.4/include/meson.build mesa-26.1.4-banan_os/include/meson.build
|
|
--- mesa-26.1.4/include/meson.build 2026-07-01 17:16:13.000000000 +0300
|
|
+++ mesa-26.1.4-banan_os/include/meson.build 2026-07-05 04:20:13.934374363 +0300
|
|
@@ -78,6 +78,10 @@
|
|
)
|
|
endif
|
|
|
|
+if with_osmesa
|
|
+ install_headers('GL/osmesa.h', subdir : 'GL')
|
|
+endif
|
|
+
|
|
if with_dri
|
|
install_headers('GL/internal/dri_interface.h', subdir : 'GL/internal')
|
|
endif
|
|
diff -ruN mesa-26.1.4/meson.build mesa-26.1.4-banan_os/meson.build
|
|
--- mesa-26.1.4/meson.build 2026-07-01 17:16:13.000000000 +0300
|
|
+++ mesa-26.1.4-banan_os/meson.build 2026-07-05 04:20:13.894859382 +0300
|
|
@@ -107,6 +107,7 @@
|
|
with_aco_tests = get_option('build-aco-tests')
|
|
with_glx_read_only_text = get_option('glx-read-only-text')
|
|
with_glx_direct = get_option('glx-direct')
|
|
+with_osmesa = get_option('osmesa')
|
|
with_vulkan_overlay_layer = get_option('vulkan-layers').contains('overlay')
|
|
with_vulkan_device_select_layer = get_option('vulkan-layers').contains('device-select')
|
|
with_vulkan_screenshot_layer = get_option('vulkan-layers').contains('screenshot')
|
|
@@ -2152,6 +2153,17 @@
|
|
pre_args += '-DHAVE_LIBUNWIND'
|
|
endif
|
|
|
|
+if with_osmesa
|
|
+ if not with_gallium_swrast
|
|
+ error('OSMesa gallium requires gallium softpipe or llvmpipe.')
|
|
+ endif
|
|
+ if host_machine.system() == 'windows'
|
|
+ osmesa_lib_name = 'osmesa'
|
|
+ else
|
|
+ osmesa_lib_name = 'OSMesa'
|
|
+ endif
|
|
+endif
|
|
+
|
|
# TODO: symbol mangling
|
|
|
|
with_wayland_bind_display = with_platform_wayland and get_option('legacy-wayland').contains('bind-wayland-display')
|
|
@@ -2567,6 +2579,7 @@
|
|
gallium_frontends += 'rusticl'
|
|
endif
|
|
gallium_summary += {'Frontends': gallium_frontends}
|
|
+ gallium_summary += {'Off-screen rendering (OSMesa)': with_osmesa ? 'lib' + osmesa_lib_name : false}
|
|
gallium_summary += {'HUD lm-sensors': dep_lmsensors.found()}
|
|
endif
|
|
summary(gallium_summary, section: 'Gallium', bool_yn: true, list_sep: ' ')
|
|
diff -ruN mesa-26.1.4/meson.options mesa-26.1.4-banan_os/meson.options
|
|
--- mesa-26.1.4/meson.options 2026-07-01 17:16:13.000000000 +0300
|
|
+++ mesa-26.1.4-banan_os/meson.options 2026-07-05 04:20:13.784091541 +0300
|
|
@@ -547,6 +547,13 @@
|
|
)
|
|
|
|
option(
|
|
+ 'osmesa',
|
|
+ type : 'boolean',
|
|
+ value : false,
|
|
+ description : 'Build OSmesa.'
|
|
+)
|
|
+
|
|
+option(
|
|
'tools',
|
|
type : 'array',
|
|
value : [],
|
|
diff -ruN mesa-26.1.4/src/gallium/frontends/osmesa/meson.build mesa-26.1.4-banan_os/src/gallium/frontends/osmesa/meson.build
|
|
--- mesa-26.1.4/src/gallium/frontends/osmesa/meson.build 1970-01-01 02:00:00.000000000 +0200
|
|
+++ mesa-26.1.4-banan_os/src/gallium/frontends/osmesa/meson.build 2026-07-05 04:20:14.131684982 +0300
|
|
@@ -0,0 +1,19 @@
|
|
+# Copyright © 2017-2018 Intel Corporation
|
|
+# SPDX-License-Identifier: MIT
|
|
+
|
|
+osmesa_st_c_args = []
|
|
+if with_platform_windows
|
|
+ if not with_shared_glapi
|
|
+ osmesa_st_c_args += ['-D_GLAPI_NO_EXPORTS']
|
|
+ endif
|
|
+endif
|
|
+
|
|
+libosmesa_st = static_library(
|
|
+ 'osmesa_st',
|
|
+ 'osmesa.c',
|
|
+ c_args : osmesa_st_c_args,
|
|
+ include_directories : [
|
|
+ inc_include, inc_src, inc_gallium, inc_gallium_aux, inc_mesa,
|
|
+ ],
|
|
+ dependencies : [idep_mesautil],
|
|
+)
|
|
diff -ruN mesa-26.1.4/src/gallium/frontends/osmesa/osmesa.c mesa-26.1.4-banan_os/src/gallium/frontends/osmesa/osmesa.c
|
|
--- mesa-26.1.4/src/gallium/frontends/osmesa/osmesa.c 1970-01-01 02:00:00.000000000 +0200
|
|
+++ mesa-26.1.4-banan_os/src/gallium/frontends/osmesa/osmesa.c 2026-07-05 04:20:14.131719693 +0300
|
|
@@ -0,0 +1,1029 @@
|
|
+/*
|
|
+ * Copyright (c) 2013 Brian Paul All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be included
|
|
+ * in all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
+ * OTHER DEALINGS IN THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+
|
|
+/*
|
|
+ * Off-Screen rendering into client memory.
|
|
+ * OpenGL gallium frontend for softpipe and llvmpipe.
|
|
+ *
|
|
+ * Notes:
|
|
+ *
|
|
+ * If Gallium is built with LLVM support we use the llvmpipe driver.
|
|
+ * Otherwise we use softpipe. The GALLIUM_DRIVER environment variable
|
|
+ * may be set to "softpipe" or "llvmpipe" to override.
|
|
+ *
|
|
+ * With softpipe we could render directly into the user's buffer by using a
|
|
+ * display target resource. However, softpipe doesn't support "upside-down"
|
|
+ * rendering which would be needed for the OSMESA_Y_UP=TRUE case.
|
|
+ *
|
|
+ * With llvmpipe we could only render directly into the user's buffer when its
|
|
+ * width and height is a multiple of the tile size (64 pixels).
|
|
+ *
|
|
+ * Because of these constraints we always render into ordinary resources then
|
|
+ * copy the results to the user's buffer in the flush_front() function which
|
|
+ * is called when the app calls glFlush/Finish.
|
|
+ *
|
|
+ * In general, the OSMesa interface is pretty ugly and not a good match
|
|
+ * for Gallium. But we're interested in doing the best we can to preserve
|
|
+ * application portability. With a little work we could come up with a
|
|
+ * much nicer, new off-screen Gallium interface...
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * The following block is for avoid windows.h to be included
|
|
+ * and osmesa require APIENTRY to be defined
|
|
+ */
|
|
+#include "util/glheader.h"
|
|
+#ifndef APIENTRY
|
|
+#define APIENTRY GLAPIENTRY
|
|
+#endif
|
|
+#include "GL/osmesa.h"
|
|
+
|
|
+#include <stdio.h>
|
|
+#include <c11/threads.h>
|
|
+
|
|
+#include "state_tracker/st_context.h"
|
|
+
|
|
+#include "glapi/glapi/glapi.h" /* for OSMesaGetProcAddress below */
|
|
+
|
|
+#include "pipe/p_context.h"
|
|
+#include "pipe/p_screen.h"
|
|
+#include "pipe/p_state.h"
|
|
+
|
|
+#include "util/u_atomic.h"
|
|
+#include "util/box.h"
|
|
+#include "util/u_debug.h"
|
|
+#include "util/format/u_format.h"
|
|
+#include "util/u_inlines.h"
|
|
+#include "util/u_memory.h"
|
|
+
|
|
+#include "postprocess/filters.h"
|
|
+#include "postprocess/postprocess.h"
|
|
+
|
|
+#include "frontend/api.h"
|
|
+
|
|
+
|
|
+
|
|
+extern struct pipe_screen *
|
|
+osmesa_create_screen(void);
|
|
+
|
|
+
|
|
+
|
|
+struct osmesa_buffer
|
|
+{
|
|
+ struct pipe_frontend_drawable base;
|
|
+ struct st_visual visual;
|
|
+ unsigned width, height;
|
|
+
|
|
+ struct pipe_resource *textures[ST_ATTACHMENT_COUNT];
|
|
+
|
|
+ void *map;
|
|
+
|
|
+ struct osmesa_buffer *next; /**< next in linked list */
|
|
+};
|
|
+
|
|
+
|
|
+struct osmesa_context
|
|
+{
|
|
+ struct st_context *st;
|
|
+
|
|
+ bool ever_used; /*< Has this context ever been current? */
|
|
+
|
|
+ struct osmesa_buffer *current_buffer;
|
|
+
|
|
+ /* Storage for depth/stencil, if the user has requested access. The backing
|
|
+ * driver always has its own storage for the actual depth/stencil, which we
|
|
+ * have to transfer in and out.
|
|
+ */
|
|
+ void *zs;
|
|
+ unsigned zs_stride;
|
|
+
|
|
+ enum pipe_format depth_stencil_format, accum_format;
|
|
+
|
|
+ GLenum format; /*< User-specified context format */
|
|
+ GLenum type; /*< Buffer's data type */
|
|
+ GLint user_row_length; /*< user-specified number of pixels per row */
|
|
+ GLboolean y_up; /*< TRUE -> Y increases upward */
|
|
+ /*< FALSE -> Y increases downward */
|
|
+
|
|
+ /** Which postprocessing filters are enabled. */
|
|
+ unsigned pp_enabled[PP_FILTERS];
|
|
+ struct pp_queue_t *pp;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * Called from the ST manager.
|
|
+ */
|
|
+static int
|
|
+osmesa_st_get_param(struct pipe_frontend_screen *fscreen, enum st_manager_param param)
|
|
+{
|
|
+ /* no-op */
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct pipe_frontend_screen *global_fscreen = NULL;
|
|
+
|
|
+static void
|
|
+destroy_st_manager(void)
|
|
+{
|
|
+ if (global_fscreen) {
|
|
+ if (global_fscreen->screen)
|
|
+ global_fscreen->screen->destroy(global_fscreen->screen);
|
|
+ FREE(global_fscreen);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+create_st_manager(void)
|
|
+{
|
|
+ if (atexit(destroy_st_manager) != 0)
|
|
+ return;
|
|
+
|
|
+ global_fscreen = CALLOC_STRUCT(pipe_frontend_screen);
|
|
+ if (global_fscreen) {
|
|
+ global_fscreen->screen = osmesa_create_screen();
|
|
+ global_fscreen->get_param = osmesa_st_get_param;
|
|
+ global_fscreen->get_egl_image = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * Create/return a singleton st_manager object.
|
|
+ */
|
|
+static struct pipe_frontend_screen *
|
|
+get_st_manager(void)
|
|
+{
|
|
+ static once_flag create_once_flag = ONCE_FLAG_INIT;
|
|
+
|
|
+ call_once(&create_once_flag, create_st_manager);
|
|
+
|
|
+ return global_fscreen;
|
|
+}
|
|
+
|
|
+/* Reads the color or depth buffer from the backing context to either the user storage
|
|
+ * (color buffer) or our temporary (z/s)
|
|
+ */
|
|
+static void
|
|
+osmesa_read_buffer(OSMesaContext osmesa, struct pipe_resource *res, void *dst,
|
|
+ int dst_stride, bool y_up)
|
|
+{
|
|
+ struct pipe_context *pipe = osmesa->st->pipe;
|
|
+
|
|
+ struct pipe_box box;
|
|
+ u_box_2d(0, 0, res->width0, res->height0, &box);
|
|
+
|
|
+ struct pipe_transfer *transfer = NULL;
|
|
+ uint8_t *src = pipe->texture_map(pipe, res, 0, PIPE_MAP_READ, &box,
|
|
+ &transfer);
|
|
+
|
|
+ /*
|
|
+ * Copy the color buffer from the resource to the user's buffer.
|
|
+ */
|
|
+
|
|
+ if (y_up) {
|
|
+ /* need to flip image upside down */
|
|
+ dst = (uint8_t *)dst + (res->height0 - 1) * dst_stride;
|
|
+ dst_stride = -dst_stride;
|
|
+ }
|
|
+
|
|
+ unsigned bpp = util_format_get_blocksize(res->format);
|
|
+ for (unsigned y = 0; y < res->height0; y++)
|
|
+ {
|
|
+ memcpy(dst, src, bpp * res->width0);
|
|
+ dst = (uint8_t *)dst + dst_stride;
|
|
+ src += transfer->stride;
|
|
+ }
|
|
+
|
|
+ pipe->texture_unmap(pipe, transfer);
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * Given an OSMESA_x format and a GL_y type, return the best
|
|
+ * matching PIPE_FORMAT_z.
|
|
+ * Note that we can't exactly match all user format/type combinations
|
|
+ * with gallium formats. If we find this to be a problem, we can
|
|
+ * implement more elaborate format/type conversion in the flush_front()
|
|
+ * function.
|
|
+ */
|
|
+static enum pipe_format
|
|
+osmesa_choose_format(GLenum format, GLenum type)
|
|
+{
|
|
+ switch (format) {
|
|
+ case OSMESA_RGBA:
|
|
+ if (type == GL_UNSIGNED_BYTE) {
|
|
+#if UTIL_ARCH_LITTLE_ENDIAN
|
|
+ return PIPE_FORMAT_R8G8B8A8_UNORM;
|
|
+#else
|
|
+ return PIPE_FORMAT_A8B8G8R8_UNORM;
|
|
+#endif
|
|
+ }
|
|
+ else if (type == GL_UNSIGNED_SHORT) {
|
|
+ return PIPE_FORMAT_R16G16B16A16_UNORM;
|
|
+ }
|
|
+ else if (type == GL_FLOAT) {
|
|
+ return PIPE_FORMAT_R32G32B32A32_FLOAT;
|
|
+ }
|
|
+ else {
|
|
+ return PIPE_FORMAT_NONE;
|
|
+ }
|
|
+ break;
|
|
+ case OSMESA_BGRA:
|
|
+ if (type == GL_UNSIGNED_BYTE) {
|
|
+#if UTIL_ARCH_LITTLE_ENDIAN
|
|
+ return PIPE_FORMAT_B8G8R8A8_UNORM;
|
|
+#else
|
|
+ return PIPE_FORMAT_A8R8G8B8_UNORM;
|
|
+#endif
|
|
+ }
|
|
+ else if (type == GL_UNSIGNED_SHORT) {
|
|
+ return PIPE_FORMAT_R16G16B16A16_UNORM;
|
|
+ }
|
|
+ else if (type == GL_FLOAT) {
|
|
+ return PIPE_FORMAT_R32G32B32A32_FLOAT;
|
|
+ }
|
|
+ else {
|
|
+ return PIPE_FORMAT_NONE;
|
|
+ }
|
|
+ break;
|
|
+ case OSMESA_ARGB:
|
|
+ if (type == GL_UNSIGNED_BYTE) {
|
|
+#if UTIL_ARCH_LITTLE_ENDIAN
|
|
+ return PIPE_FORMAT_A8R8G8B8_UNORM;
|
|
+#else
|
|
+ return PIPE_FORMAT_B8G8R8A8_UNORM;
|
|
+#endif
|
|
+ }
|
|
+ else if (type == GL_UNSIGNED_SHORT) {
|
|
+ return PIPE_FORMAT_R16G16B16A16_UNORM;
|
|
+ }
|
|
+ else if (type == GL_FLOAT) {
|
|
+ return PIPE_FORMAT_R32G32B32A32_FLOAT;
|
|
+ }
|
|
+ else {
|
|
+ return PIPE_FORMAT_NONE;
|
|
+ }
|
|
+ break;
|
|
+ case OSMESA_RGB:
|
|
+ if (type == GL_UNSIGNED_BYTE) {
|
|
+ return PIPE_FORMAT_R8G8B8_UNORM;
|
|
+ }
|
|
+ else if (type == GL_UNSIGNED_SHORT) {
|
|
+ return PIPE_FORMAT_R16G16B16_UNORM;
|
|
+ }
|
|
+ else if (type == GL_FLOAT) {
|
|
+ return PIPE_FORMAT_R32G32B32_FLOAT;
|
|
+ }
|
|
+ else {
|
|
+ return PIPE_FORMAT_NONE;
|
|
+ }
|
|
+ break;
|
|
+ case OSMESA_BGR:
|
|
+ /* No gallium format for this one */
|
|
+ return PIPE_FORMAT_NONE;
|
|
+ case OSMESA_RGB_565:
|
|
+ if (type != GL_UNSIGNED_SHORT_5_6_5)
|
|
+ return PIPE_FORMAT_NONE;
|
|
+ return PIPE_FORMAT_B5G6R5_UNORM;
|
|
+ default:
|
|
+ return PIPE_FORMAT_NONE;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * Initialize an st_visual object.
|
|
+ */
|
|
+static void
|
|
+osmesa_init_st_visual(struct st_visual *vis,
|
|
+ enum pipe_format color_format,
|
|
+ enum pipe_format ds_format,
|
|
+ enum pipe_format accum_format)
|
|
+{
|
|
+ vis->buffer_mask = ST_ATTACHMENT_FRONT_LEFT_MASK;
|
|
+
|
|
+ if (ds_format != PIPE_FORMAT_NONE)
|
|
+ vis->buffer_mask |= ST_ATTACHMENT_DEPTH_STENCIL_MASK;
|
|
+ if (accum_format != PIPE_FORMAT_NONE)
|
|
+ vis->buffer_mask |= ST_ATTACHMENT_ACCUM;
|
|
+
|
|
+ vis->color_format = color_format;
|
|
+ vis->depth_stencil_format = ds_format;
|
|
+ vis->accum_format = accum_format;
|
|
+ vis->samples = 1;
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * Return the osmesa_buffer that corresponds to an pipe_frontend_drawable.
|
|
+ */
|
|
+static inline struct osmesa_buffer *
|
|
+drawable_to_osbuffer(struct pipe_frontend_drawable *drawable)
|
|
+{
|
|
+ return (struct osmesa_buffer *)drawable;
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * Called via glFlush/glFinish. This is where we copy the contents
|
|
+ * of the driver's color buffer into the user-specified buffer.
|
|
+ */
|
|
+static bool
|
|
+osmesa_st_framebuffer_flush_front(struct st_context *st,
|
|
+ struct pipe_frontend_drawable *drawable,
|
|
+ enum st_attachment_type statt)
|
|
+{
|
|
+ OSMesaContext osmesa = OSMesaGetCurrentContext();
|
|
+ struct osmesa_buffer *osbuffer = drawable_to_osbuffer(drawable);
|
|
+ struct pipe_resource *res = osbuffer->textures[statt];
|
|
+ unsigned bpp;
|
|
+ int dst_stride;
|
|
+
|
|
+ if (statt != ST_ATTACHMENT_FRONT_LEFT)
|
|
+ return false;
|
|
+
|
|
+ if (osmesa->pp) {
|
|
+ struct pipe_resource *zsbuf = NULL;
|
|
+ unsigned i;
|
|
+
|
|
+ /* Find the z/stencil buffer if there is one */
|
|
+ for (i = 0; i < ARRAY_SIZE(osbuffer->textures); i++) {
|
|
+ struct pipe_resource *res = osbuffer->textures[i];
|
|
+ if (res) {
|
|
+ const struct util_format_description *desc =
|
|
+ util_format_description(res->format);
|
|
+
|
|
+ if (util_format_has_depth(desc)) {
|
|
+ zsbuf = res;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* run the postprocess stage(s) */
|
|
+ pp_run(osmesa->pp, res, res, zsbuf);
|
|
+ }
|
|
+
|
|
+ /* Snapshot the color buffer to the user's buffer. */
|
|
+ bpp = util_format_get_blocksize(osbuffer->visual.color_format);
|
|
+ if (osmesa->user_row_length)
|
|
+ dst_stride = bpp * osmesa->user_row_length;
|
|
+ else
|
|
+ dst_stride = bpp * osbuffer->width;
|
|
+
|
|
+ osmesa_read_buffer(osmesa, res, osbuffer->map, dst_stride, osmesa->y_up);
|
|
+
|
|
+ /* If the user has requested the Z/S buffer, then snapshot that one too. */
|
|
+ if (osmesa->zs) {
|
|
+ osmesa_read_buffer(osmesa, osbuffer->textures[ST_ATTACHMENT_DEPTH_STENCIL],
|
|
+ osmesa->zs, osmesa->zs_stride, true);
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * Called by the st manager to validate the framebuffer (allocate
|
|
+ * its resources).
|
|
+ */
|
|
+static bool
|
|
+osmesa_st_framebuffer_validate(struct st_context *st,
|
|
+ struct pipe_frontend_drawable *drawable,
|
|
+ const enum st_attachment_type *statts,
|
|
+ unsigned count,
|
|
+ struct pipe_resource **out,
|
|
+ struct pipe_resource **resolve)
|
|
+{
|
|
+ struct pipe_screen *screen = get_st_manager()->screen;
|
|
+ enum st_attachment_type i;
|
|
+ struct osmesa_buffer *osbuffer = drawable_to_osbuffer(drawable);
|
|
+ struct pipe_resource templat;
|
|
+
|
|
+ memset(&templat, 0, sizeof(templat));
|
|
+ templat.target = PIPE_TEXTURE_RECT;
|
|
+ templat.format = 0; /* setup below */
|
|
+ templat.last_level = 0;
|
|
+ templat.width0 = osbuffer->width;
|
|
+ templat.height0 = osbuffer->height;
|
|
+ templat.depth0 = 1;
|
|
+ templat.array_size = 1;
|
|
+ templat.usage = PIPE_USAGE_DEFAULT;
|
|
+ templat.bind = 0; /* setup below */
|
|
+ templat.flags = 0;
|
|
+
|
|
+ for (i = 0; i < count; i++) {
|
|
+ enum pipe_format format = PIPE_FORMAT_NONE;
|
|
+ unsigned bind = 0;
|
|
+
|
|
+ /*
|
|
+ * At this time, we really only need to handle the front-left color
|
|
+ * attachment, since that's all we specified for the visual in
|
|
+ * osmesa_init_st_visual().
|
|
+ */
|
|
+ if (statts[i] == ST_ATTACHMENT_FRONT_LEFT) {
|
|
+ format = osbuffer->visual.color_format;
|
|
+ bind = PIPE_BIND_RENDER_TARGET;
|
|
+ }
|
|
+ else if (statts[i] == ST_ATTACHMENT_DEPTH_STENCIL) {
|
|
+ format = osbuffer->visual.depth_stencil_format;
|
|
+ bind = PIPE_BIND_DEPTH_STENCIL;
|
|
+ }
|
|
+ else if (statts[i] == ST_ATTACHMENT_ACCUM) {
|
|
+ format = osbuffer->visual.accum_format;
|
|
+ bind = PIPE_BIND_RENDER_TARGET;
|
|
+ }
|
|
+ else {
|
|
+ debug_warning("Unexpected attachment type in "
|
|
+ "osmesa_st_framebuffer_validate()");
|
|
+ }
|
|
+
|
|
+ templat.format = format;
|
|
+ templat.bind = bind;
|
|
+ pipe_resource_reference(&out[i], NULL);
|
|
+ out[i] = osbuffer->textures[statts[i]] =
|
|
+ screen->resource_create(screen, &templat);
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static uint32_t osmesa_fb_ID = 0;
|
|
+
|
|
+
|
|
+/**
|
|
+ * Create new buffer and add to linked list.
|
|
+ */
|
|
+static struct osmesa_buffer *
|
|
+osmesa_create_buffer(enum pipe_format color_format,
|
|
+ enum pipe_format ds_format,
|
|
+ enum pipe_format accum_format)
|
|
+{
|
|
+ struct osmesa_buffer *osbuffer = CALLOC_STRUCT(osmesa_buffer);
|
|
+ if (osbuffer) {
|
|
+ osbuffer->base.flush_front = osmesa_st_framebuffer_flush_front;
|
|
+ osbuffer->base.validate = osmesa_st_framebuffer_validate;
|
|
+ p_atomic_set(&osbuffer->base.stamp, 1);
|
|
+ osbuffer->base.ID = p_atomic_inc_return(&osmesa_fb_ID);
|
|
+ osbuffer->base.fscreen = get_st_manager();
|
|
+ osbuffer->base.visual = &osbuffer->visual;
|
|
+
|
|
+ osmesa_init_st_visual(&osbuffer->visual, color_format,
|
|
+ ds_format, accum_format);
|
|
+ }
|
|
+
|
|
+ return osbuffer;
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+osmesa_destroy_buffer(struct osmesa_buffer *osbuffer)
|
|
+{
|
|
+ /*
|
|
+ * Notify the state manager that the associated framebuffer interface
|
|
+ * is no longer valid.
|
|
+ */
|
|
+ st_api_destroy_drawable(&osbuffer->base);
|
|
+
|
|
+ FREE(osbuffer);
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+/**********************************************************************/
|
|
+/***** Public Functions *****/
|
|
+/**********************************************************************/
|
|
+
|
|
+
|
|
+/**
|
|
+ * Create an Off-Screen Mesa rendering context. The only attribute needed is
|
|
+ * an RGBA vs Color-Index mode flag.
|
|
+ *
|
|
+ * Input: format - Must be GL_RGBA
|
|
+ * sharelist - specifies another OSMesaContext with which to share
|
|
+ * display lists. NULL indicates no sharing.
|
|
+ * Return: an OSMesaContext or 0 if error
|
|
+ */
|
|
+GLAPI OSMesaContext GLAPIENTRY
|
|
+OSMesaCreateContext(GLenum format, OSMesaContext sharelist)
|
|
+{
|
|
+ return OSMesaCreateContextExt(format, 24, 8, 0, sharelist);
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * New in Mesa 3.5
|
|
+ *
|
|
+ * Create context and specify size of ancillary buffers.
|
|
+ */
|
|
+GLAPI OSMesaContext GLAPIENTRY
|
|
+OSMesaCreateContextExt(GLenum format, GLint depthBits, GLint stencilBits,
|
|
+ GLint accumBits, OSMesaContext sharelist)
|
|
+{
|
|
+ int attribs[100], n = 0;
|
|
+
|
|
+ attribs[n++] = OSMESA_FORMAT;
|
|
+ attribs[n++] = format;
|
|
+ attribs[n++] = OSMESA_DEPTH_BITS;
|
|
+ attribs[n++] = depthBits;
|
|
+ attribs[n++] = OSMESA_STENCIL_BITS;
|
|
+ attribs[n++] = stencilBits;
|
|
+ attribs[n++] = OSMESA_ACCUM_BITS;
|
|
+ attribs[n++] = accumBits;
|
|
+ attribs[n++] = 0;
|
|
+
|
|
+ return OSMesaCreateContextAttribs(attribs, sharelist);
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * New in Mesa 11.2
|
|
+ *
|
|
+ * Create context with attribute list.
|
|
+ */
|
|
+GLAPI OSMesaContext GLAPIENTRY
|
|
+OSMesaCreateContextAttribs(const int *attribList, OSMesaContext sharelist)
|
|
+{
|
|
+ OSMesaContext osmesa;
|
|
+ struct st_context *st_shared;
|
|
+ enum st_context_error st_error = 0;
|
|
+ struct st_context_attribs attribs;
|
|
+ GLenum format = GL_RGBA;
|
|
+ int depthBits = 0, stencilBits = 0, accumBits = 0;
|
|
+ int profile = OSMESA_COMPAT_PROFILE, version_major = 1, version_minor = 0;
|
|
+ int i;
|
|
+
|
|
+ if (sharelist) {
|
|
+ st_shared = sharelist->st;
|
|
+ }
|
|
+ else {
|
|
+ st_shared = NULL;
|
|
+ }
|
|
+
|
|
+ for (i = 0; attribList[i]; i += 2) {
|
|
+ switch (attribList[i]) {
|
|
+ case OSMESA_FORMAT:
|
|
+ format = attribList[i+1];
|
|
+ switch (format) {
|
|
+ case OSMESA_COLOR_INDEX:
|
|
+ case OSMESA_RGBA:
|
|
+ case OSMESA_BGRA:
|
|
+ case OSMESA_ARGB:
|
|
+ case OSMESA_RGB:
|
|
+ case OSMESA_BGR:
|
|
+ case OSMESA_RGB_565:
|
|
+ /* legal */
|
|
+ break;
|
|
+ default:
|
|
+ return NULL;
|
|
+ }
|
|
+ break;
|
|
+ case OSMESA_DEPTH_BITS:
|
|
+ depthBits = attribList[i+1];
|
|
+ if (depthBits < 0)
|
|
+ return NULL;
|
|
+ break;
|
|
+ case OSMESA_STENCIL_BITS:
|
|
+ stencilBits = attribList[i+1];
|
|
+ if (stencilBits < 0)
|
|
+ return NULL;
|
|
+ break;
|
|
+ case OSMESA_ACCUM_BITS:
|
|
+ accumBits = attribList[i+1];
|
|
+ if (accumBits < 0)
|
|
+ return NULL;
|
|
+ break;
|
|
+ case OSMESA_PROFILE:
|
|
+ profile = attribList[i+1];
|
|
+ if (profile != OSMESA_CORE_PROFILE &&
|
|
+ profile != OSMESA_COMPAT_PROFILE)
|
|
+ return NULL;
|
|
+ break;
|
|
+ case OSMESA_CONTEXT_MAJOR_VERSION:
|
|
+ version_major = attribList[i+1];
|
|
+ if (version_major < 1)
|
|
+ return NULL;
|
|
+ break;
|
|
+ case OSMESA_CONTEXT_MINOR_VERSION:
|
|
+ version_minor = attribList[i+1];
|
|
+ if (version_minor < 0)
|
|
+ return NULL;
|
|
+ break;
|
|
+ case 0:
|
|
+ /* end of list */
|
|
+ break;
|
|
+ default:
|
|
+ fprintf(stderr, "Bad attribute in OSMesaCreateContextAttribs()\n");
|
|
+ return NULL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ osmesa = (OSMesaContext) CALLOC_STRUCT(osmesa_context);
|
|
+ if (!osmesa)
|
|
+ return NULL;
|
|
+
|
|
+ /* Choose depth/stencil/accum buffer formats */
|
|
+ if (accumBits > 0) {
|
|
+ osmesa->accum_format = PIPE_FORMAT_R16G16B16A16_SNORM;
|
|
+ }
|
|
+ if (depthBits > 0 && stencilBits > 0) {
|
|
+ osmesa->depth_stencil_format = PIPE_FORMAT_Z24_UNORM_S8_UINT;
|
|
+ }
|
|
+ else if (stencilBits > 0) {
|
|
+ osmesa->depth_stencil_format = PIPE_FORMAT_S8_UINT;
|
|
+ }
|
|
+ else if (depthBits >= 24) {
|
|
+ osmesa->depth_stencil_format = PIPE_FORMAT_Z24X8_UNORM;
|
|
+ }
|
|
+ else if (depthBits >= 16) {
|
|
+ osmesa->depth_stencil_format = PIPE_FORMAT_Z16_UNORM;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Create the rendering context
|
|
+ */
|
|
+ memset(&attribs, 0, sizeof(attribs));
|
|
+ attribs.profile = (profile == OSMESA_CORE_PROFILE)
|
|
+ ? API_OPENGL_CORE : API_OPENGL_COMPAT;
|
|
+ attribs.major = version_major;
|
|
+ attribs.minor = version_minor;
|
|
+ attribs.flags = 0; /* ST_CONTEXT_FLAG_x */
|
|
+ attribs.options.force_glsl_extensions_warn = false;
|
|
+ attribs.options.disable_blend_func_extended = false;
|
|
+ attribs.options.disable_glsl_line_continuations = false;
|
|
+ attribs.options.force_glsl_version = 0;
|
|
+
|
|
+ osmesa_init_st_visual(&attribs.visual,
|
|
+ PIPE_FORMAT_NONE,
|
|
+ osmesa->depth_stencil_format,
|
|
+ osmesa->accum_format);
|
|
+
|
|
+ osmesa->st = st_api_create_context(get_st_manager(),
|
|
+ &attribs, &st_error, st_shared);
|
|
+ if (!osmesa->st) {
|
|
+ FREE(osmesa);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ osmesa->st->frontend_context = osmesa;
|
|
+
|
|
+ osmesa->format = format;
|
|
+ osmesa->user_row_length = 0;
|
|
+ osmesa->y_up = GL_TRUE;
|
|
+
|
|
+ return osmesa;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+/**
|
|
+ * Destroy an Off-Screen Mesa rendering context.
|
|
+ *
|
|
+ * \param osmesa the context to destroy
|
|
+ */
|
|
+GLAPI void GLAPIENTRY
|
|
+OSMesaDestroyContext(OSMesaContext osmesa)
|
|
+{
|
|
+ if (osmesa) {
|
|
+ pp_free(osmesa->pp);
|
|
+ st_destroy_context(osmesa->st);
|
|
+ free(osmesa->zs);
|
|
+ FREE(osmesa);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * Bind an OSMesaContext to an image buffer. The image buffer is just a
|
|
+ * block of memory which the client provides. Its size must be at least
|
|
+ * as large as width*height*pixelSize. Its address should be a multiple
|
|
+ * of 4 if using RGBA mode.
|
|
+ *
|
|
+ * By default, image data is stored in the order of glDrawPixels: row-major
|
|
+ * order with the lower-left image pixel stored in the first array position
|
|
+ * (ie. bottom-to-top).
|
|
+ *
|
|
+ * If the context's viewport hasn't been initialized yet, it will now be
|
|
+ * initialized to (0,0,width,height).
|
|
+ *
|
|
+ * Input: osmesa - the rendering context
|
|
+ * buffer - the image buffer memory
|
|
+ * type - data type for pixel components
|
|
+ * GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT
|
|
+ * or GL_FLOAT.
|
|
+ * width, height - size of image buffer in pixels, at least 1
|
|
+ * Return: GL_TRUE if success, GL_FALSE if error because of invalid osmesa,
|
|
+ * invalid type, invalid size, etc.
|
|
+ */
|
|
+GLAPI GLboolean GLAPIENTRY
|
|
+OSMesaMakeCurrent(OSMesaContext osmesa, void *buffer, GLenum type,
|
|
+ GLsizei width, GLsizei height)
|
|
+{
|
|
+ enum pipe_format color_format;
|
|
+
|
|
+ if (!osmesa && !buffer) {
|
|
+ st_api_make_current(NULL, NULL, NULL);
|
|
+ return GL_TRUE;
|
|
+ }
|
|
+
|
|
+ if (!osmesa || !buffer || width < 1 || height < 1) {
|
|
+ return GL_FALSE;
|
|
+ }
|
|
+
|
|
+ color_format = osmesa_choose_format(osmesa->format, type);
|
|
+ if (color_format == PIPE_FORMAT_NONE) {
|
|
+ fprintf(stderr, "OSMesaMakeCurrent(unsupported format/type)\n");
|
|
+ return GL_FALSE;
|
|
+ }
|
|
+
|
|
+ /* See if we already have a buffer that uses these pixel formats */
|
|
+ if (osmesa->current_buffer &&
|
|
+ (osmesa->current_buffer->visual.color_format != color_format ||
|
|
+ osmesa->current_buffer->visual.depth_stencil_format != osmesa->depth_stencil_format ||
|
|
+ osmesa->current_buffer->visual.accum_format != osmesa->accum_format ||
|
|
+ osmesa->current_buffer->width != width ||
|
|
+ osmesa->current_buffer->height != height)) {
|
|
+ osmesa_destroy_buffer(osmesa->current_buffer);
|
|
+ osmesa->current_buffer = NULL;
|
|
+ }
|
|
+
|
|
+ if (!osmesa->current_buffer) {
|
|
+ osmesa->current_buffer = osmesa_create_buffer(color_format,
|
|
+ osmesa->depth_stencil_format,
|
|
+ osmesa->accum_format);
|
|
+ }
|
|
+
|
|
+ struct osmesa_buffer *osbuffer = osmesa->current_buffer;
|
|
+
|
|
+ osbuffer->width = width;
|
|
+ osbuffer->height = height;
|
|
+ osbuffer->map = buffer;
|
|
+
|
|
+ osmesa->type = type;
|
|
+
|
|
+ st_api_make_current(osmesa->st, &osbuffer->base, &osbuffer->base);
|
|
+
|
|
+ /* XXX: We should probably load the current color value into the buffer here
|
|
+ * to match classic swrast behavior (context's fb starts with the contents of
|
|
+ * your pixel buffer).
|
|
+ */
|
|
+
|
|
+ if (!osmesa->ever_used) {
|
|
+ /* one-time init, just postprocessing for now */
|
|
+ bool any_pp_enabled = false;
|
|
+ unsigned i;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(osmesa->pp_enabled); i++) {
|
|
+ if (osmesa->pp_enabled[i]) {
|
|
+ any_pp_enabled = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (any_pp_enabled) {
|
|
+ osmesa->pp = pp_init(osmesa->st->pipe,
|
|
+ osmesa->pp_enabled,
|
|
+ osmesa->st->cso_context,
|
|
+ osmesa->st,
|
|
+ st_context_invalidate_state);
|
|
+
|
|
+ pp_init_fbos(osmesa->pp, width, height);
|
|
+ }
|
|
+
|
|
+ osmesa->ever_used = true;
|
|
+ }
|
|
+
|
|
+ return GL_TRUE;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+GLAPI OSMesaContext GLAPIENTRY
|
|
+OSMesaGetCurrentContext(void)
|
|
+{
|
|
+ struct st_context *st = st_api_get_current();
|
|
+ return st ? (OSMesaContext) st->frontend_context : NULL;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+GLAPI void GLAPIENTRY
|
|
+OSMesaPixelStore(GLint pname, GLint value)
|
|
+{
|
|
+ OSMesaContext osmesa = OSMesaGetCurrentContext();
|
|
+
|
|
+ switch (pname) {
|
|
+ case OSMESA_ROW_LENGTH:
|
|
+ osmesa->user_row_length = value;
|
|
+ break;
|
|
+ case OSMESA_Y_UP:
|
|
+ osmesa->y_up = value ? GL_TRUE : GL_FALSE;
|
|
+ break;
|
|
+ default:
|
|
+ fprintf(stderr, "Invalid pname in OSMesaPixelStore()\n");
|
|
+ return;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+GLAPI void GLAPIENTRY
|
|
+OSMesaGetIntegerv(GLint pname, GLint *value)
|
|
+{
|
|
+ OSMesaContext osmesa = OSMesaGetCurrentContext();
|
|
+ struct osmesa_buffer *osbuffer = osmesa ? osmesa->current_buffer : NULL;
|
|
+
|
|
+ switch (pname) {
|
|
+ case OSMESA_WIDTH:
|
|
+ *value = osbuffer ? osbuffer->width : 0;
|
|
+ return;
|
|
+ case OSMESA_HEIGHT:
|
|
+ *value = osbuffer ? osbuffer->height : 0;
|
|
+ return;
|
|
+ case OSMESA_FORMAT:
|
|
+ *value = osmesa->format;
|
|
+ return;
|
|
+ case OSMESA_TYPE:
|
|
+ /* current color buffer's data type */
|
|
+ *value = osmesa->type;
|
|
+ return;
|
|
+ case OSMESA_ROW_LENGTH:
|
|
+ *value = osmesa->user_row_length;
|
|
+ return;
|
|
+ case OSMESA_Y_UP:
|
|
+ *value = osmesa->y_up;
|
|
+ return;
|
|
+ case OSMESA_MAX_WIDTH:
|
|
+ FALLTHROUGH;
|
|
+ case OSMESA_MAX_HEIGHT:
|
|
+ {
|
|
+ struct pipe_screen *screen = get_st_manager()->screen;
|
|
+ *value = screen->caps.max_texture_2d_size;
|
|
+ }
|
|
+ return;
|
|
+ default:
|
|
+ fprintf(stderr, "Invalid pname in OSMesaGetIntegerv()\n");
|
|
+ return;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * Return information about the depth buffer associated with an OSMesa context.
|
|
+ * Input: c - the OSMesa context
|
|
+ * Output: width, height - size of buffer in pixels
|
|
+ * bytesPerValue - bytes per depth value (2 or 4)
|
|
+ * buffer - pointer to depth buffer values
|
|
+ * Return: GL_TRUE or GL_FALSE to indicate success or failure.
|
|
+ */
|
|
+GLAPI GLboolean GLAPIENTRY
|
|
+OSMesaGetDepthBuffer(OSMesaContext c, GLint *width, GLint *height,
|
|
+ GLint *bytesPerValue, void **buffer)
|
|
+{
|
|
+ struct osmesa_buffer *osbuffer = c->current_buffer;
|
|
+ struct pipe_resource *res = osbuffer->textures[ST_ATTACHMENT_DEPTH_STENCIL];
|
|
+
|
|
+ if (!res) {
|
|
+ *width = 0;
|
|
+ *height = 0;
|
|
+ *bytesPerValue = 0;
|
|
+ *buffer = NULL;
|
|
+ return GL_FALSE;
|
|
+ }
|
|
+
|
|
+ *width = res->width0;
|
|
+ *height = res->height0;
|
|
+ *bytesPerValue = util_format_get_blocksize(res->format);
|
|
+
|
|
+ if (!c->zs) {
|
|
+ c->zs_stride = *width * *bytesPerValue;
|
|
+ c->zs = calloc(c->zs_stride, *height);
|
|
+ if (!c->zs)
|
|
+ return GL_FALSE;
|
|
+
|
|
+ osmesa_read_buffer(c, res, c->zs, c->zs_stride, true);
|
|
+ }
|
|
+
|
|
+ *buffer = c->zs;
|
|
+
|
|
+ return GL_TRUE;
|
|
+}
|
|
+
|
|
+
|
|
+/**
|
|
+ * Return the color buffer associated with an OSMesa context.
|
|
+ * Input: c - the OSMesa context
|
|
+ * Output: width, height - size of buffer in pixels
|
|
+ * format - the pixel format (OSMESA_FORMAT)
|
|
+ * buffer - pointer to color buffer values
|
|
+ * Return: GL_TRUE or GL_FALSE to indicate success or failure.
|
|
+ */
|
|
+GLAPI GLboolean GLAPIENTRY
|
|
+OSMesaGetColorBuffer(OSMesaContext osmesa, GLint *width,
|
|
+ GLint *height, GLint *format, void **buffer)
|
|
+{
|
|
+ struct osmesa_buffer *osbuffer = osmesa->current_buffer;
|
|
+
|
|
+ if (osbuffer) {
|
|
+ *width = osbuffer->width;
|
|
+ *height = osbuffer->height;
|
|
+ *format = osmesa->format;
|
|
+ *buffer = osbuffer->map;
|
|
+ return GL_TRUE;
|
|
+ }
|
|
+ else {
|
|
+ *width = 0;
|
|
+ *height = 0;
|
|
+ *format = 0;
|
|
+ *buffer = 0;
|
|
+ return GL_FALSE;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+struct name_function
|
|
+{
|
|
+ const char *Name;
|
|
+ OSMESAproc Function;
|
|
+};
|
|
+
|
|
+static struct name_function functions[] = {
|
|
+ { "OSMesaCreateContext", (OSMESAproc) OSMesaCreateContext },
|
|
+ { "OSMesaCreateContextExt", (OSMESAproc) OSMesaCreateContextExt },
|
|
+ { "OSMesaCreateContextAttribs", (OSMESAproc) OSMesaCreateContextAttribs },
|
|
+ { "OSMesaDestroyContext", (OSMESAproc) OSMesaDestroyContext },
|
|
+ { "OSMesaMakeCurrent", (OSMESAproc) OSMesaMakeCurrent },
|
|
+ { "OSMesaGetCurrentContext", (OSMESAproc) OSMesaGetCurrentContext },
|
|
+ { "OSMesaPixelStore", (OSMESAproc) OSMesaPixelStore },
|
|
+ { "OSMesaGetIntegerv", (OSMESAproc) OSMesaGetIntegerv },
|
|
+ { "OSMesaGetDepthBuffer", (OSMESAproc) OSMesaGetDepthBuffer },
|
|
+ { "OSMesaGetColorBuffer", (OSMESAproc) OSMesaGetColorBuffer },
|
|
+ { "OSMesaGetProcAddress", (OSMESAproc) OSMesaGetProcAddress },
|
|
+ { "OSMesaColorClamp", (OSMESAproc) OSMesaColorClamp },
|
|
+ { "OSMesaPostprocess", (OSMESAproc) OSMesaPostprocess },
|
|
+ { NULL, NULL }
|
|
+};
|
|
+
|
|
+
|
|
+GLAPI OSMESAproc GLAPIENTRY
|
|
+OSMesaGetProcAddress(const char *funcName)
|
|
+{
|
|
+ int i;
|
|
+ for (i = 0; functions[i].Name; i++) {
|
|
+ if (strcmp(functions[i].Name, funcName) == 0)
|
|
+ return functions[i].Function;
|
|
+ }
|
|
+ return _mesa_glapi_get_proc_address(funcName);
|
|
+}
|
|
+
|
|
+
|
|
+GLAPI void GLAPIENTRY
|
|
+OSMesaColorClamp(GLboolean enable)
|
|
+{
|
|
+ extern void GLAPIENTRY _mesa_ClampColor(GLenum target, GLenum clamp);
|
|
+
|
|
+ _mesa_ClampColor(GL_CLAMP_FRAGMENT_COLOR_ARB,
|
|
+ enable ? GL_TRUE : GL_FIXED_ONLY_ARB);
|
|
+}
|
|
+
|
|
+
|
|
+GLAPI void GLAPIENTRY
|
|
+OSMesaPostprocess(OSMesaContext osmesa, const char *filter,
|
|
+ unsigned enable_value)
|
|
+{
|
|
+ if (!osmesa->ever_used) {
|
|
+ /* We can only enable/disable postprocess filters before a context
|
|
+ * is made current for the first time.
|
|
+ */
|
|
+ unsigned i;
|
|
+
|
|
+ for (i = 0; i < PP_FILTERS; i++) {
|
|
+ if (strcmp(pp_filters[i].name, filter) == 0) {
|
|
+ osmesa->pp_enabled[i] = enable_value;
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ debug_warning("OSMesaPostprocess(unknown filter)\n");
|
|
+ }
|
|
+ else {
|
|
+ debug_warning("Calling OSMesaPostprocess() after OSMesaMakeCurrent()\n");
|
|
+ }
|
|
+}
|
|
diff -ruN mesa-26.1.4/src/gallium/meson.build mesa-26.1.4-banan_os/src/gallium/meson.build
|
|
--- mesa-26.1.4/src/gallium/meson.build 2026-07-01 17:16:13.000000000 +0300
|
|
+++ mesa-26.1.4-banan_os/src/gallium/meson.build 2026-07-05 04:20:14.035181849 +0300
|
|
@@ -232,6 +232,10 @@
|
|
subdir('frontends/dri')
|
|
subdir('targets/dri')
|
|
endif
|
|
+if with_osmesa
|
|
+ subdir('frontends/osmesa')
|
|
+ subdir('targets/osmesa')
|
|
+endif
|
|
if with_platform_haiku
|
|
subdir('frontends/hgl')
|
|
endif
|
|
diff -ruN mesa-26.1.4/src/gallium/targets/osmesa/meson.build mesa-26.1.4-banan_os/src/gallium/targets/osmesa/meson.build
|
|
--- mesa-26.1.4/src/gallium/targets/osmesa/meson.build 1970-01-01 02:00:00.000000000 +0200
|
|
+++ mesa-26.1.4-banan_os/src/gallium/targets/osmesa/meson.build 2026-07-05 04:20:14.212936332 +0300
|
|
@@ -0,0 +1,36 @@
|
|
+# Copyright © 2017 Intel Corporation
|
|
+# SPDX-License-Identifier: MIT
|
|
+
|
|
+osmesa_link_args = []
|
|
+osmesa_link_deps = []
|
|
+
|
|
+if with_ld_version_script
|
|
+ osmesa_link_args += ['-Wl,--version-script', join_paths(meson.current_source_dir(), 'osmesa.sym')]
|
|
+ osmesa_link_deps += files('osmesa.sym')
|
|
+endif
|
|
+
|
|
+libosmesa = shared_library(
|
|
+ osmesa_lib_name,
|
|
+ files('osmesa_target.c'),
|
|
+ include_directories : [
|
|
+ inc_include, inc_src, inc_gallium, inc_gallium_aux, inc_gallium_winsys,
|
|
+ inc_gallium_drivers,
|
|
+ ],
|
|
+ gnu_symbol_visibility : 'hidden',
|
|
+ link_args : [ld_args_gc_sections, osmesa_link_args],
|
|
+ link_depends : osmesa_link_deps,
|
|
+ link_whole : [libosmesa_st, libglapi_bridge],
|
|
+ link_with : [libmesa, libgallium, libglapi, libws_null],
|
|
+ dependencies : [dep_ws2_32, dep_thread, dep_clock, dep_unwind, driver_swrast, idep_mesautil],
|
|
+ install : true,
|
|
+ version : '8.0.0',
|
|
+ darwin_versions : '9.0.0',
|
|
+)
|
|
+
|
|
+pkg.generate(
|
|
+ name : 'osmesa',
|
|
+ description : 'Mesa Off-screen Rendering Library',
|
|
+ version : '8.0.0',
|
|
+ libraries : libosmesa,
|
|
+ libraries_private : gl_priv_libs,
|
|
+)
|
|
diff -ruN mesa-26.1.4/src/gallium/targets/osmesa/osmesa.sym mesa-26.1.4-banan_os/src/gallium/targets/osmesa/osmesa.sym
|
|
--- mesa-26.1.4/src/gallium/targets/osmesa/osmesa.sym 1970-01-01 02:00:00.000000000 +0200
|
|
+++ mesa-26.1.4-banan_os/src/gallium/targets/osmesa/osmesa.sym 2026-07-05 04:20:14.212992485 +0300
|
|
@@ -0,0 +1,19 @@
|
|
+{
|
|
+ global:
|
|
+ OSMesaColorClamp;
|
|
+ OSMesaCreateContext;
|
|
+ OSMesaCreateContextAttribs;
|
|
+ OSMesaCreateContextExt;
|
|
+ OSMesaDestroyContext;
|
|
+ OSMesaGetColorBuffer;
|
|
+ OSMesaGetCurrentContext;
|
|
+ OSMesaGetDepthBuffer;
|
|
+ OSMesaGetIntegerv;
|
|
+ OSMesaGetProcAddress;
|
|
+ OSMesaMakeCurrent;
|
|
+ OSMesaPixelStore;
|
|
+ OSMesaPostprocess;
|
|
+ gl*;
|
|
+ local:
|
|
+ *;
|
|
+};
|
|
diff -ruN mesa-26.1.4/src/gallium/targets/osmesa/osmesa_target.c mesa-26.1.4-banan_os/src/gallium/targets/osmesa/osmesa_target.c
|
|
--- mesa-26.1.4/src/gallium/targets/osmesa/osmesa_target.c 1970-01-01 02:00:00.000000000 +0200
|
|
+++ mesa-26.1.4-banan_os/src/gallium/targets/osmesa/osmesa_target.c 2026-07-05 04:20:14.213049196 +0300
|
|
@@ -0,0 +1,56 @@
|
|
+/*
|
|
+ * Copyright (c) 2013 Brian Paul All Rights Reserved.
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a
|
|
+ * copy of this software and associated documentation files (the "Software"),
|
|
+ * to deal in the Software without restriction, including without limitation
|
|
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
+ * and/or sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be included
|
|
+ * in all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
+ * OTHER DEALINGS IN THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+
|
|
+#include "target-helpers/inline_sw_helper.h"
|
|
+#include "target-helpers/inline_debug_helper.h"
|
|
+
|
|
+#include "sw/null/null_sw_winsys.h"
|
|
+
|
|
+
|
|
+struct pipe_screen *
|
|
+osmesa_create_screen(void);
|
|
+
|
|
+
|
|
+struct pipe_screen *
|
|
+osmesa_create_screen(void)
|
|
+{
|
|
+ struct sw_winsys *winsys;
|
|
+ struct pipe_screen *screen;
|
|
+
|
|
+ /* We use a null software winsys since we always just render to ordinary
|
|
+ * driver resources.
|
|
+ */
|
|
+ winsys = null_sw_create();
|
|
+ if (!winsys)
|
|
+ return NULL;
|
|
+
|
|
+ /* Create llvmpipe or softpipe screen */
|
|
+ screen = sw_screen_create(winsys);
|
|
+ if (!screen) {
|
|
+ winsys->destroy(winsys);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ /* Inject optional trace, debug, etc. wrappers */
|
|
+ return debug_screen_wrap(screen);
|
|
+}
|