mirror of https://github.com/FWGS/xash3d-fwgs
Merge 38a3154de0
into 60c6767337
This commit is contained in:
commit
35d75cd6ea
|
@ -18,17 +18,20 @@ GNU General Public License for more details.
|
|||
// video backends (XASH_VIDEO)
|
||||
#define VIDEO_NULL 0
|
||||
#define VIDEO_SDL 1
|
||||
#define VIDEO_ANDROID 2
|
||||
#define VIDEO_FBDEV 3
|
||||
#define VIDEO_DOS 4
|
||||
|
||||
// audio backends (XASH_SOUND)
|
||||
#define SOUND_NULL 0
|
||||
#define SOUND_SDL 1
|
||||
#define SOUND_OPENSLES 2
|
||||
#define SOUND_ALSA 3
|
||||
|
||||
// input (XASH_INPUT)
|
||||
#define INPUT_NULL 0
|
||||
#define INPUT_SDL 1
|
||||
#define INPUT_ANDROID 2
|
||||
#define INPUT_EVDEV 3
|
||||
|
||||
// timer (XASH_TIMER)
|
||||
|
@ -41,6 +44,7 @@ GNU General Public License for more details.
|
|||
// messageboxes (XASH_MESSAGEBOX)
|
||||
#define MSGBOX_STDERR 0
|
||||
#define MSGBOX_SDL 1
|
||||
#define MSGBOX_ANDROID 2
|
||||
#define MSGBOX_WIN32 3
|
||||
#define MSGBOX_NSWITCH 4
|
||||
|
||||
|
|
|
@ -52,6 +52,25 @@ SETUP BACKENDS DEFINITIONS
|
|||
#endif
|
||||
#endif // XASH_MESSAGEBOX
|
||||
#endif
|
||||
#elif XASH_ANDROID
|
||||
// we are building for Android platform, use Android APIs
|
||||
#ifndef XASH_VIDEO
|
||||
#define XASH_VIDEO VIDEO_ANDROID
|
||||
#endif // XASH_VIDEO
|
||||
|
||||
#ifndef XASH_INPUT
|
||||
#define XASH_INPUT INPUT_ANDROID
|
||||
#endif // XASH_INPUT
|
||||
|
||||
#ifndef XASH_SOUND
|
||||
#define XASH_SOUND SOUND_OPENSLES
|
||||
#endif // XASH_SOUND
|
||||
|
||||
#ifndef XASH_MESSAGEBOX
|
||||
#define XASH_MESSAGEBOX MSGBOX_ANDROID
|
||||
#endif // XASH_MESSAGEBOX
|
||||
|
||||
#define XASH_USE_EVDEV 1
|
||||
#elif XASH_LINUX
|
||||
// we are building for Linux without SDL2, can draw only to framebuffer yet
|
||||
#ifndef XASH_VIDEO
|
||||
|
@ -151,8 +170,6 @@ Default build-depended cvar and constant values
|
|||
#define DEFAULT_MODE_WIDTH 960
|
||||
#define DEFAULT_MODE_HEIGHT 544
|
||||
#define DEFAULT_ALLOWCONSOLE 1
|
||||
#elif XASH_ANDROID
|
||||
#define DEFAULT_TOUCH_ENABLE "1"
|
||||
#elif XASH_MOBILE_PLATFORM
|
||||
#define DEFAULT_TOUCH_ENABLE "1"
|
||||
#define DEFAULT_M_IGNORE "1"
|
||||
|
|
|
@ -14,7 +14,7 @@ GNU General Public License for more details.
|
|||
*/
|
||||
#include "platform/platform.h"
|
||||
|
||||
#if !defined(XASH_DEDICATED)
|
||||
#if !defined(XASH_DEDICATED) && XASH_SDL
|
||||
|
||||
#include "input.h"
|
||||
#include "client.h"
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
#ifndef ANDROID_PRIV_H
|
||||
#define ANDROID_PRIV_H
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <android/log.h>
|
||||
#include <jni.h>
|
||||
|
||||
extern struct jnimethods_s
|
||||
{
|
||||
jclass actcls;
|
||||
jclass bindcls;
|
||||
JavaVM *vm;
|
||||
JNIEnv *env;
|
||||
jmethodID enableTextInput;
|
||||
jmethodID vibrate;
|
||||
jmethodID messageBox;
|
||||
jmethodID notify;
|
||||
jmethodID setTitle;
|
||||
jmethodID setIcon;
|
||||
jmethodID getAndroidId;
|
||||
jmethodID saveID;
|
||||
jmethodID loadID;
|
||||
jmethodID showMouse;
|
||||
jmethodID shellExecute;
|
||||
jmethodID swapBuffers;
|
||||
jmethodID toggleEGL;
|
||||
jmethodID createGLContext;
|
||||
jmethodID getGLAttribute;
|
||||
jmethodID deleteGLContext;
|
||||
jmethodID getSurface;
|
||||
jmethodID preShutdown;
|
||||
int width, height;
|
||||
} jni;
|
||||
|
||||
typedef enum surfacestate_e
|
||||
{
|
||||
surface_pause,
|
||||
surface_active,
|
||||
surface_dummy,
|
||||
|
||||
} surfacestate_t;
|
||||
|
||||
|
||||
extern struct jnimouse_s
|
||||
{
|
||||
float x, y;
|
||||
} jnimouse;
|
||||
|
||||
//
|
||||
// vid_android.c
|
||||
//
|
||||
void Android_UpdateSurface( surfacestate_t state );
|
||||
|
||||
#endif // ANDROID_PRIV_H
|
|
@ -25,30 +25,36 @@
|
|||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if defined __ANDROID__ && !defined XASH_64BIT
|
||||
#include "build.h"
|
||||
#if !XASH_64BIT
|
||||
#include <string.h>
|
||||
#include <android/log.h>
|
||||
#include <dlfcn.h>
|
||||
#include "linker.h"
|
||||
extern "C" {
|
||||
#include "lib_android.h"
|
||||
}
|
||||
|
||||
static Elf_Sym* soinfo_elf_lookup(soinfo* si, unsigned hash, const char* name) {
|
||||
static Elf_Sym* soinfo_elf_lookup( soinfo* si, unsigned hash, const char* name )
|
||||
{
|
||||
Elf_Sym* symtab = si->symtab;
|
||||
const char* strtab = si->strtab;
|
||||
|
||||
if( si->nbucket == 0 )
|
||||
return NULL;
|
||||
|
||||
for (unsigned n = si->bucket[hash % si->nbucket]; n != 0; n = si->chain[n]) {
|
||||
for( unsigned n = si->bucket[hash % si->nbucket]; n != 0; n = si->chain[n] )
|
||||
{
|
||||
Elf_Sym* s = symtab + n;
|
||||
if (strcmp(strtab + s->st_name, name)) continue;
|
||||
if( strcmp( strtab + s->st_name, name )) continue;
|
||||
|
||||
/* only concern ourselves with global and weak symbol definitions */
|
||||
switch (ELF_ST_BIND(s->st_info)) {
|
||||
switch( ELF_ST_BIND( s->st_info ))
|
||||
{
|
||||
case STB_GLOBAL:
|
||||
case STB_WEAK:
|
||||
if (s->st_shndx == SHN_UNDEF) {
|
||||
if( s->st_shndx == SHN_UNDEF )
|
||||
continue;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
@ -56,11 +62,44 @@ static Elf_Sym* soinfo_elf_lookup(soinfo* si, unsigned hash, const char* name) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static unsigned elfhash(const char* _name) {
|
||||
const unsigned char* name = (const unsigned char*) _name;
|
||||
static Elf_Sym* soinfo_elf_lookup_reverse(soinfo* si, size_t addr)
|
||||
{
|
||||
Elf_Sym* symtab = si->symtab;
|
||||
|
||||
if( si->nbucket == 0 )
|
||||
return NULL;
|
||||
|
||||
for( int j = 0; j < si->nbucket; j++ )
|
||||
{
|
||||
for( unsigned n = si->bucket[j]; n != 0; n = si->chain[n] )
|
||||
{
|
||||
Elf_Sym* s = symtab + n;
|
||||
if( s->st_value != addr )continue;
|
||||
|
||||
/* only concern ourselves with global and weak symbol definitions */
|
||||
switch( ELF_ST_BIND( s->st_info ))
|
||||
{
|
||||
case STB_GLOBAL:
|
||||
case STB_LOCAL:
|
||||
case STB_WEAK:
|
||||
if (s->st_shndx == SHN_UNDEF)
|
||||
continue;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static unsigned elfhash( const char* _name )
|
||||
{
|
||||
const unsigned char* name = ( const unsigned char* ) _name;
|
||||
unsigned h = 0, g;
|
||||
|
||||
while(*name) {
|
||||
while(*name)
|
||||
{
|
||||
h = (h << 4) + *name++;
|
||||
g = h & 0xf0000000;
|
||||
h ^= g;
|
||||
|
@ -78,21 +117,73 @@ static unsigned elfhash(const char* _name) {
|
|||
Binary Interface) where in Chapter 5 it discuss resolving "Shared
|
||||
Object Dependencies" in breadth first search order.
|
||||
*/
|
||||
static Elf_Sym* dlsym_handle_lookup(soinfo* si, const char* name) {
|
||||
return soinfo_elf_lookup(si, elfhash(name), name);
|
||||
static Elf_Sym* dlsym_handle_lookup( soinfo* si, const char* name )
|
||||
{
|
||||
return soinfo_elf_lookup( si, elfhash( name ), name );
|
||||
}
|
||||
|
||||
extern "C" void* dlsym_weak(void* handle, const char* symbol) {
|
||||
static int dladdr_fallback( const void *addr, Dl_info *info )
|
||||
{
|
||||
static soinfo *server_info;
|
||||
Elf_Sym *sym;
|
||||
|
||||
if( !server_info )
|
||||
server_info = ( soinfo* )ANDROID_GetServerLibrary();
|
||||
if( !server_info )
|
||||
return 0;
|
||||
//__android_log_print( ANDROID_LOG_ERROR, "dladdr_fb", "%p %p\n", addr, server_info );
|
||||
|
||||
sym = soinfo_elf_lookup_reverse( server_info, ((char*)addr) - ((char*)server_info->base ));
|
||||
//__android_log_print( ANDROID_LOG_ERROR, "dladdr_fb", "sym %p %p\n", addr, sym );
|
||||
if( sym )
|
||||
{
|
||||
info->dli_sname = server_info->strtab + sym->st_name;
|
||||
info->dli_fname = "server";
|
||||
info->dli_fbase = (void*)server_info->base;
|
||||
info->dli_saddr = (void*)addr;
|
||||
//__android_log_print( ANDROID_LOG_ERROR, "dladdr_fb", "name %p %s\n", addr, info->dli_sname );
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C" int __attribute__((visibility("hidden"))) dladdr( const void *addr, Dl_info *info )
|
||||
{
|
||||
typedef int (*PFNDLADDR)( const void *addr, Dl_info *info );
|
||||
PFNDLADDR pfn_dladdr;
|
||||
|
||||
if( !pfn_dladdr )
|
||||
{
|
||||
/* android does not have libdl, but have soinfo record for it from linker
|
||||
* use dlopen to get this record directly */
|
||||
void *lib = dlopen( "libdl.so", RTLD_NOW );
|
||||
|
||||
if( lib )
|
||||
pfn_dladdr = (PFNDLADDR)dlsym( lib, "dladdr" );
|
||||
if( pfn_dladdr == (PFNDLADDR)dladdr )
|
||||
pfn_dladdr = 0;
|
||||
if( !pfn_dladdr )
|
||||
pfn_dladdr = (PFNDLADDR)dladdr_fallback;
|
||||
}
|
||||
|
||||
return pfn_dladdr( addr, info );
|
||||
}
|
||||
|
||||
|
||||
extern "C" void* dlsym_weak( void* handle, const char* symbol ) {
|
||||
|
||||
soinfo* found = NULL;
|
||||
Elf_Sym* sym = NULL;
|
||||
found = reinterpret_cast<soinfo*>(handle);
|
||||
sym = dlsym_handle_lookup(found, symbol);
|
||||
found = reinterpret_cast<soinfo*>( handle );
|
||||
sym = dlsym_handle_lookup( found, symbol );
|
||||
|
||||
if (sym != NULL) {
|
||||
return reinterpret_cast<void*>(sym->st_value + found->base/*load_bias*/);
|
||||
if ( sym != NULL ) {
|
||||
return reinterpret_cast<void*>( sym->st_value + found->base /*load_bias*/ );
|
||||
}
|
||||
__android_log_print(ANDROID_LOG_ERROR, "dlsym-weak", "Failed when looking up %s\n", symbol);
|
||||
__android_log_print( ANDROID_LOG_ERROR, "dlsym-weak", "Failed when looking up %s\n", symbol );
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,453 @@
|
|||
/*
|
||||
eglutil.c - EGL context utility
|
||||
Copyright (C) 2023 mittorn
|
||||
|
||||
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 3 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.
|
||||
*/
|
||||
|
||||
#include "platform/platform.h"
|
||||
#if !defined(XASH_DEDICATED) && !XASH_SDL
|
||||
#include "eglutil.h"
|
||||
#include "client.h"
|
||||
#include "vid_common.h"
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
struct eglapi_s egl;
|
||||
|
||||
#undef GetProcAddress // windows.h?
|
||||
#define EGL_FF(x) {"egl"#x, (void*)&egl.x}
|
||||
static dllfunc_t egl_funcs[] =
|
||||
{
|
||||
EGL_FF(SwapInterval),
|
||||
EGL_FF(SwapBuffers),
|
||||
EGL_FF(GetError),
|
||||
EGL_FF(GetCurrentDisplay),
|
||||
EGL_FF(GetCurrentSurface),
|
||||
EGL_FF(GetProcAddress),
|
||||
EGL_FF(GetConfigAttrib),
|
||||
EGL_FF(GetDisplay),
|
||||
EGL_FF(Initialize),
|
||||
EGL_FF(Terminate),
|
||||
EGL_FF(QueryString),
|
||||
EGL_FF(ChooseConfig),
|
||||
EGL_FF(CreateWindowSurface),
|
||||
EGL_FF(CreateContext),
|
||||
EGL_FF(DestroyContext),
|
||||
EGL_FF(MakeCurrent),
|
||||
EGL_FF(BindAPI),
|
||||
EGL_FF(DestroySurface),
|
||||
{ NULL, NULL }
|
||||
};
|
||||
#undef EGL_FF
|
||||
static dll_info_t egl_info = { "libEGL.so", egl_funcs, false };
|
||||
|
||||
struct eglstate_s eglstate;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
|
||||
refapi/egl wrappers
|
||||
|
||||
===================
|
||||
*/
|
||||
|
||||
int EGL_SetAttribute( int attr, int val )
|
||||
{
|
||||
if( attr < 0 || attr >= REF_GL_ATTRIBUTES_COUNT )
|
||||
return -1;
|
||||
|
||||
eglstate.gl_attribs[attr] = val;
|
||||
eglstate.gl_attribs_set[attr] = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define COPY_ATTR_IF_SET( refattr, attr ) \
|
||||
if( eglstate.gl_attribs_set[refattr] ) \
|
||||
{ \
|
||||
attribs[i++] = attr; \
|
||||
attribs[i++] = eglstate.gl_attribs[refattr]; \
|
||||
}
|
||||
|
||||
size_t EGL_GenerateConfig( EGLint *attribs, size_t size )
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
memset( attribs, 0, size * sizeof( EGLint ) );
|
||||
eglstate.gles1 = false;
|
||||
memset( eglstate.gl_attribs, 0, sizeof( eglstate.gl_attribs ));
|
||||
memset( eglstate.gl_attribs_set, 0, sizeof( eglstate.gl_attribs_set ));
|
||||
|
||||
// refdll can request some attributes
|
||||
ref.dllFuncs.GL_SetupAttributes( glw_state.safe );
|
||||
|
||||
COPY_ATTR_IF_SET( REF_GL_RED_SIZE, EGL_RED_SIZE );
|
||||
COPY_ATTR_IF_SET( REF_GL_GREEN_SIZE, EGL_GREEN_SIZE );
|
||||
COPY_ATTR_IF_SET( REF_GL_BLUE_SIZE, EGL_BLUE_SIZE );
|
||||
COPY_ATTR_IF_SET( REF_GL_ALPHA_SIZE, EGL_ALPHA_SIZE );
|
||||
COPY_ATTR_IF_SET( REF_GL_DEPTH_SIZE, EGL_DEPTH_SIZE );
|
||||
COPY_ATTR_IF_SET( REF_GL_STENCIL_SIZE, EGL_STENCIL_SIZE );
|
||||
COPY_ATTR_IF_SET( REF_GL_MULTISAMPLEBUFFERS, EGL_SAMPLE_BUFFERS );
|
||||
COPY_ATTR_IF_SET( REF_GL_MULTISAMPLESAMPLES, EGL_SAMPLES );
|
||||
|
||||
if( eglstate.gl_attribs_set[REF_GL_ACCELERATED_VISUAL] )
|
||||
{
|
||||
attribs[i++] = EGL_CONFIG_CAVEAT;
|
||||
attribs[i++] = eglstate.gl_attribs[REF_GL_ACCELERATED_VISUAL] ? EGL_NONE : EGL_DONT_CARE;
|
||||
}
|
||||
|
||||
// BigGL support
|
||||
attribs[i++] = EGL_RENDERABLE_TYPE;
|
||||
eglstate.gl_api = EGL_OPENGL_ES_API;
|
||||
|
||||
if( eglstate.gl_attribs_set[REF_GL_CONTEXT_PROFILE_MASK] &&
|
||||
!( eglstate.gl_attribs[REF_GL_CONTEXT_PROFILE_MASK] & REF_GL_CONTEXT_PROFILE_ES ))
|
||||
{
|
||||
attribs[i++] = EGL_OPENGL_BIT;
|
||||
eglstate.gl_api = EGL_OPENGL_API;
|
||||
}
|
||||
else if( eglstate.gl_attribs_set[REF_GL_CONTEXT_MAJOR_VERSION] &&
|
||||
eglstate.gl_attribs[REF_GL_CONTEXT_MAJOR_VERSION] >= 2 )
|
||||
{
|
||||
attribs[i++] = EGL_OPENGL_ES2_BIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
i--; // erase EGL_RENDERABLE_TYPE
|
||||
eglstate.gles1 = true;
|
||||
}
|
||||
|
||||
attribs[i++] = EGL_NONE;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
size_t EGL_GenerateContextConfig( EGLint *attribs, size_t size )
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
memset( attribs, 0, size * sizeof( EGLint ));
|
||||
|
||||
if( Q_strstr( eglstate.extensions, "EGL_KHR_create_context") )
|
||||
{
|
||||
Con_DPrintf( S_NOTE "EGL_KHR_create_context found, setting additional context flags\n");
|
||||
|
||||
if( eglstate.gl_attribs_set[REF_GL_CONTEXT_FLAGS] )
|
||||
{
|
||||
attribs[i++] = 0x30FC; // EGL_CONTEXT_FLAGS_KHR
|
||||
attribs[i++] = eglstate.gl_attribs[REF_GL_CONTEXT_FLAGS] & ((REF_GL_CONTEXT_ROBUST_ACCESS_FLAG << 1) - 1);
|
||||
}
|
||||
|
||||
if( eglstate.gl_attribs_set[REF_GL_CONTEXT_PROFILE_MASK] )
|
||||
{
|
||||
int val = eglstate.gl_attribs[REF_GL_CONTEXT_PROFILE_MASK];
|
||||
|
||||
if( val & ( (REF_GL_CONTEXT_PROFILE_COMPATIBILITY << 1) - 1 ) )
|
||||
{
|
||||
attribs[i++] = 0x30FD; // EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR;
|
||||
attribs[i++] = val;
|
||||
}
|
||||
}
|
||||
|
||||
COPY_ATTR_IF_SET( REF_GL_CONTEXT_MAJOR_VERSION, EGL_CONTEXT_CLIENT_VERSION );
|
||||
COPY_ATTR_IF_SET( REF_GL_CONTEXT_MINOR_VERSION, 0x30FB );
|
||||
}
|
||||
else
|
||||
{
|
||||
// without extension we can set only major version
|
||||
COPY_ATTR_IF_SET( REF_GL_CONTEXT_MAJOR_VERSION, EGL_CONTEXT_CLIENT_VERSION );
|
||||
if( eglstate.gl_attribs_set[REF_GL_CONTEXT_FLAGS] && (eglstate.gl_attribs[REF_GL_CONTEXT_FLAGS] & REF_GL_CONTEXT_DEBUG_FLAG ))
|
||||
{
|
||||
attribs[i++] = 0x31B0; // EGL_CONTEXT_OPENGL_DEBUG;
|
||||
attribs[i++] = EGL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
attribs[i++] = EGL_NONE;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=========================
|
||||
EGL_GetAttribute
|
||||
|
||||
query
|
||||
=========================
|
||||
*/
|
||||
int EGL_GetAttribute( int attr, int *val )
|
||||
{
|
||||
EGLBoolean ret;
|
||||
|
||||
if( attr < 0 || attr >= REF_GL_ATTRIBUTES_COUNT )
|
||||
return -1;
|
||||
|
||||
if( !val )
|
||||
return -1;
|
||||
|
||||
switch( attr )
|
||||
{
|
||||
case REF_GL_RED_SIZE:
|
||||
ret = egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_RED_SIZE, val );
|
||||
return 0;
|
||||
case REF_GL_GREEN_SIZE:
|
||||
ret = egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_GREEN_SIZE, val );
|
||||
return 0;
|
||||
case REF_GL_BLUE_SIZE:
|
||||
ret = egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_BLUE_SIZE, val );
|
||||
return 0;
|
||||
case REF_GL_ALPHA_SIZE:
|
||||
ret = egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_ALPHA_SIZE, val );
|
||||
return 0;
|
||||
case REF_GL_DEPTH_SIZE:
|
||||
ret = egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_DEPTH_SIZE, val );
|
||||
return 0;
|
||||
case REF_GL_STENCIL_SIZE:
|
||||
ret = egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_STENCIL_SIZE, val );
|
||||
return 0;
|
||||
case REF_GL_MULTISAMPLESAMPLES:
|
||||
ret = egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_SAMPLES, val );
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=========================
|
||||
EGL_UpdateSurface
|
||||
|
||||
destroy old surface, recreate and make context current
|
||||
must be called with valid context
|
||||
=========================
|
||||
*/
|
||||
qboolean EGL_UpdateSurface( void *window, qboolean dummy )
|
||||
{
|
||||
if( !eglstate.valid )
|
||||
return false;
|
||||
|
||||
egl.MakeCurrent( eglstate.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
|
||||
host.status = HOST_SLEEP;
|
||||
|
||||
if( eglstate.surface )
|
||||
{
|
||||
egl.DestroySurface( eglstate.dpy, eglstate.surface );
|
||||
eglstate.surface = EGL_NO_SURFACE;
|
||||
}
|
||||
|
||||
if( !window )
|
||||
{
|
||||
|
||||
if( dummy && eglstate.support_surfaceless_context )
|
||||
{
|
||||
if( egl.MakeCurrent( eglstate.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, eglstate.context ))
|
||||
{
|
||||
Con_Reportf( S_NOTE "EGL_UpdateSurface: using surfaceless mode\n" );
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
egl.MakeCurrent( eglstate.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
|
||||
eglstate.support_surfaceless_context = false;
|
||||
}
|
||||
Con_Reportf( S_NOTE "EGL_UpdateSurface: missing native window, detaching context\n" );
|
||||
}
|
||||
|
||||
return false; // let platform fallback to dummy surface or pause engine
|
||||
}
|
||||
|
||||
if(( eglstate.surface = egl.CreateWindowSurface( eglstate.dpy, eglstate.cfg, window, NULL )) == EGL_NO_SURFACE )
|
||||
{
|
||||
Con_Reportf( S_ERROR "eglCreateWindowSurface returned error: 0x%x\n", egl.GetError() );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !egl.MakeCurrent( eglstate.dpy, eglstate.surface, eglstate.surface, eglstate.context ))
|
||||
{
|
||||
Con_Reportf( S_ERROR "eglMakeCurrent returned error: 0x%x\n", egl.GetError() );
|
||||
return false;
|
||||
}
|
||||
|
||||
Con_DPrintf( S_NOTE "restored current context\n" );
|
||||
if( !dummy)
|
||||
host.status = HOST_FRAME;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=========================
|
||||
EGL_CreateContext
|
||||
|
||||
query attributes for ref and create context
|
||||
=========================
|
||||
*/
|
||||
qboolean EGL_CreateContext( void )
|
||||
{
|
||||
EGLint attribs[32+1], contextAttribs[32+1];
|
||||
const size_t attribsSize = ARRAYSIZE( attribs );
|
||||
size_t s1, s2;
|
||||
|
||||
if( !eglstate.dpy && ( eglstate.dpy = egl.GetDisplay( EGL_DEFAULT_DISPLAY )) == EGL_NO_DISPLAY )
|
||||
{
|
||||
Con_Reportf( S_ERROR "eglGetDisplay returned error: 0x%x\n", egl.GetError() );
|
||||
return false;
|
||||
}
|
||||
|
||||
eglstate.extensions = egl.QueryString( eglstate.dpy, EGL_EXTENSIONS );
|
||||
|
||||
s1 = EGL_GenerateConfig(attribs, attribsSize);
|
||||
s2 = EGL_GenerateContextConfig(contextAttribs, attribsSize);
|
||||
|
||||
|
||||
if( !egl.BindAPI( eglstate.gl_api ))
|
||||
{
|
||||
Con_Reportf( S_ERROR "eglBindAPI returned error: 0x%x\n", egl.GetError() );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !egl.Initialize( eglstate.dpy, NULL, NULL ))
|
||||
{
|
||||
Con_Reportf( S_ERROR "eglInitialize returned error: 0x%x\n", egl.GetError() );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !egl.ChooseConfig( eglstate.dpy, attribs, &eglstate.cfg, 1, &eglstate.numCfg ))
|
||||
{
|
||||
Con_Reportf( S_ERROR "eglChooseConfig returned error: 0x%x\n", egl.GetError() );
|
||||
return false;
|
||||
}
|
||||
|
||||
if(( eglstate.context = egl.CreateContext( eglstate.dpy, eglstate.cfg, NULL, contextAttribs )) == EGL_NO_CONTEXT )
|
||||
{
|
||||
Con_Reportf( S_ERROR "eglCreateContext returned error: 0x%x\n", egl.GetError() );
|
||||
return false;
|
||||
}
|
||||
|
||||
eglstate.valid = true;
|
||||
eglstate.imported = false;
|
||||
|
||||
// now check if it's safe to use surfaceless context here without surface fallback
|
||||
if( eglstate.extensions && Q_strstr( eglstate.extensions, "EGL_KHR_surfaceless_context" ))
|
||||
eglstate.support_surfaceless_context = true;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
===========================
|
||||
EGL_ImportContext
|
||||
|
||||
import current egl context to use EGL functions
|
||||
===========================
|
||||
*/
|
||||
qboolean EGL_ImportContext( void )
|
||||
{
|
||||
if( !egl.GetCurrentDisplay )
|
||||
return false;
|
||||
|
||||
eglstate.dpy = egl.GetCurrentDisplay();
|
||||
|
||||
if( eglstate.dpy == EGL_NO_DISPLAY )
|
||||
return false;
|
||||
|
||||
eglstate.surface = egl.GetCurrentSurface( EGL_DRAW );
|
||||
|
||||
if( eglstate.surface == EGL_NO_SURFACE )
|
||||
return false;
|
||||
|
||||
// now check if swapBuffers does not give error
|
||||
if( egl.SwapBuffers( eglstate.dpy, eglstate.surface ) == EGL_FALSE )
|
||||
return false;
|
||||
|
||||
// double check
|
||||
if( egl.GetError() != EGL_SUCCESS )
|
||||
return false;
|
||||
|
||||
eglstate.extensions = egl.QueryString( eglstate.dpy, EGL_EXTENSIONS );
|
||||
|
||||
eglstate.valid = eglstate.imported = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
=========================
|
||||
EGL_Terminate
|
||||
=========================
|
||||
*/
|
||||
void EGL_Terminate( void )
|
||||
{
|
||||
egl.MakeCurrent( eglstate.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
|
||||
|
||||
egl.DestroyContext( eglstate.dpy, eglstate.context );
|
||||
|
||||
egl.DestroySurface( eglstate.dpy, eglstate.surface );
|
||||
|
||||
egl.Terminate( eglstate.dpy );
|
||||
|
||||
Sys_FreeLibrary( &egl_info );
|
||||
}
|
||||
|
||||
/*
|
||||
=========================
|
||||
EGL_GetProcAddress
|
||||
|
||||
eglGetProcAddress/dlsym wrapper
|
||||
=========================
|
||||
*/
|
||||
void *EGL_GetProcAddress( const char *name )
|
||||
{
|
||||
void *gles;
|
||||
void *addr;
|
||||
|
||||
// TODO: cross-platform loading
|
||||
if( eglstate.gles1 )
|
||||
{
|
||||
if( !eglstate.libgles1 )
|
||||
eglstate.libgles1 = dlopen( "libGLESv1_CM.so", RTLD_NOW );
|
||||
gles = eglstate.libgles1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !eglstate.libgles2 )
|
||||
eglstate.libgles2 = dlopen( "libGLESv2.so", RTLD_NOW );
|
||||
gles = eglstate.libgles2;
|
||||
}
|
||||
|
||||
if( gles && ( addr = dlsym( gles, name )))
|
||||
return addr;
|
||||
|
||||
if( !egl.GetProcAddress )
|
||||
return NULL;
|
||||
|
||||
return egl.GetProcAddress( name );
|
||||
}
|
||||
|
||||
/*
|
||||
=========================
|
||||
EGL_LoadLibrary
|
||||
=========================
|
||||
*/
|
||||
void EGL_LoadLibrary( void )
|
||||
{
|
||||
Sys_LoadLibrary( &egl_info );
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
eglutil.h - EGL context utility
|
||||
Copyright (C) 2023 mittorn
|
||||
|
||||
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 3 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.
|
||||
*/
|
||||
#ifndef EGLUTIL_H
|
||||
#define EGLUTIL_H
|
||||
#include "platform/platform.h"
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
extern struct eglstate_s
|
||||
{
|
||||
qboolean valid, imported;
|
||||
EGLDisplay dpy;
|
||||
EGLSurface surface;
|
||||
EGLContext context;
|
||||
EGLConfig cfg;
|
||||
EGLint numCfg;
|
||||
qboolean support_surfaceless_context;
|
||||
|
||||
const char *extensions;
|
||||
int gl_attribs[REF_GL_ATTRIBUTES_COUNT];
|
||||
qboolean gl_attribs_set[REF_GL_ATTRIBUTES_COUNT];
|
||||
EGLint gl_api;
|
||||
qboolean gles1;
|
||||
void *libgles1, *libgles2;
|
||||
} eglstate;
|
||||
|
||||
extern struct eglapi_s
|
||||
{
|
||||
EGLSurface (*GetCurrentSurface)( EGLint readdraw );
|
||||
EGLDisplay (*GetCurrentDisplay)( void );
|
||||
EGLint (*GetError)( void );
|
||||
EGLBoolean (*SwapBuffers)( EGLDisplay dpy, EGLSurface surface );
|
||||
EGLBoolean (*SwapInterval)( EGLDisplay dpy, EGLint interval );
|
||||
void *(*GetProcAddress)( const char *procname );
|
||||
EGLBoolean (*GetConfigAttrib)( EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value );
|
||||
EGLDisplay (*GetDisplay)( NativeDisplayType display );
|
||||
EGLBoolean (*Initialize)( EGLDisplay dpy, EGLint *major, EGLint *minor );
|
||||
EGLBoolean (*Terminate)( EGLDisplay dpy );
|
||||
const char *(*QueryString)( EGLDisplay dpy, EGLint name );
|
||||
EGLBoolean (*ChooseConfig)( EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config );
|
||||
EGLSurface (*CreateWindowSurface)( EGLDisplay dpy, EGLConfig config, NativeWindowType window, const EGLint *attrib_list );
|
||||
EGLContext (*CreateContext)( EGLDisplay dpy, EGLConfig config, EGLContext share_list, const EGLint *attrib_list );
|
||||
EGLBoolean (*DestroyContext)( EGLDisplay dpy, EGLContext ctx );
|
||||
EGLBoolean (*MakeCurrent)( EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx );
|
||||
EGLBoolean (*BindAPI)( EGLenum api );
|
||||
EGLBoolean (*DestroySurface)( EGLDisplay dpy, EGLSurface surface );
|
||||
|
||||
} egl;
|
||||
|
||||
void EGL_LoadLibrary( void );
|
||||
void * EGL_GetProcAddress( const char *name );
|
||||
void EGL_Terminate( void );
|
||||
qboolean EGL_ImportContext( void );
|
||||
qboolean EGL_CreateContext( void );
|
||||
qboolean EGL_UpdateSurface( void *window, qboolean dummy );
|
||||
int EGL_GetAttribute( int attr, int *val );
|
||||
size_t EGL_GenerateContextConfig( EGLint *attribs, size_t size );
|
||||
size_t EGL_GenerateConfig( EGLint *attribs, size_t size );
|
||||
int EGL_SetAttribute( int attr, int val );
|
||||
|
||||
#endif // EGLUTIL_H
|
|
@ -19,6 +19,12 @@ GNU General Public License for more details.
|
|||
#include "platform/android/lib_android.h"
|
||||
#include "platform/android/dlsym-weak.h" // Android < 5.0
|
||||
|
||||
void *ANDROID_GetServerLibrary( void )
|
||||
{
|
||||
return svgame.hInstance;
|
||||
}
|
||||
|
||||
|
||||
void *ANDROID_LoadLibrary( const char *dllname )
|
||||
{
|
||||
char path[MAX_SYSPATH];
|
||||
|
|
|
@ -20,6 +20,7 @@ GNU General Public License for more details.
|
|||
#define Platform_POSIX_LoadLibrary( x ) ANDROID_LoadLibrary(( x ))
|
||||
#define Platform_POSIX_GetProcAddress( x, y ) ANDROID_GetProcAddress(( x ), ( y ))
|
||||
|
||||
void *ANDROID_GetServerLibrary( void );
|
||||
void *ANDROID_LoadLibrary( const char *dllname );
|
||||
void *ANDROID_GetProcAddress( void *hInstance, const char *name );
|
||||
|
||||
|
|
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
Copyright (C) 2015 SiPlus, Chasseur de bots
|
||||
|
||||
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 "common.h"
|
||||
#include "platform/platform.h"
|
||||
#if XASH_SOUND == SOUND_OPENSLES
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include "pthread.h"
|
||||
#include "sound.h"
|
||||
|
||||
extern dma_t dma;
|
||||
|
||||
static SLObjectItf snddma_android_engine = NULL;
|
||||
static SLObjectItf snddma_android_outputMix = NULL;
|
||||
static SLObjectItf snddma_android_player = NULL;
|
||||
static SLBufferQueueItf snddma_android_bufferQueue;
|
||||
static SLPlayItf snddma_android_play;
|
||||
|
||||
static pthread_mutex_t snddma_android_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static int snddma_android_size;
|
||||
|
||||
static const SLInterfaceID *pSL_IID_ENGINE;
|
||||
static const SLInterfaceID *pSL_IID_BUFFERQUEUE;
|
||||
static const SLInterfaceID *pSL_IID_PLAY;
|
||||
static SLresult SLAPIENTRY (*pslCreateEngine)(
|
||||
SLObjectItf *pEngine,
|
||||
SLuint32 numOptions,
|
||||
const SLEngineOption *pEngineOptions,
|
||||
SLuint32 numInterfaces,
|
||||
const SLInterfaceID *pInterfaceIds,
|
||||
const SLboolean * pInterfaceRequired
|
||||
);
|
||||
|
||||
void SNDDMA_Activate( qboolean active )
|
||||
{
|
||||
if( !dma.initialized )
|
||||
return;
|
||||
|
||||
if( active )
|
||||
{
|
||||
memset( dma.buffer, 0, snddma_android_size * 2 );
|
||||
(*snddma_android_bufferQueue)->Enqueue( snddma_android_bufferQueue, dma.buffer, snddma_android_size );
|
||||
(*snddma_android_play)->SetPlayState( snddma_android_play, SL_PLAYSTATE_PLAYING );
|
||||
}
|
||||
else
|
||||
{
|
||||
(*snddma_android_play)->SetPlayState( snddma_android_play, SL_PLAYSTATE_STOPPED );
|
||||
(*snddma_android_bufferQueue)->Clear( snddma_android_bufferQueue );
|
||||
}
|
||||
}
|
||||
|
||||
static void SNDDMA_Android_Callback( SLBufferQueueItf bq, void *context )
|
||||
{
|
||||
uint8_t *buffer2;
|
||||
|
||||
pthread_mutex_lock( &snddma_android_mutex );
|
||||
|
||||
buffer2 = ( uint8_t * )dma.buffer + snddma_android_size;
|
||||
(*bq)->Enqueue( bq, buffer2, snddma_android_size );
|
||||
memcpy( buffer2, dma.buffer, snddma_android_size );
|
||||
memset( dma.buffer, 0, snddma_android_size );
|
||||
dma.samplepos += dma.samples;
|
||||
|
||||
pthread_mutex_unlock( &snddma_android_mutex );
|
||||
}
|
||||
|
||||
static const char *SNDDMA_Android_Init( void )
|
||||
{
|
||||
SLresult result;
|
||||
|
||||
SLEngineItf engine;
|
||||
|
||||
int freq;
|
||||
|
||||
SLDataLocator_BufferQueue sourceLocator;
|
||||
SLDataFormat_PCM sourceFormat;
|
||||
SLDataSource source;
|
||||
|
||||
SLDataLocator_OutputMix sinkLocator;
|
||||
SLDataSink sink;
|
||||
|
||||
SLInterfaceID interfaceID;
|
||||
SLboolean interfaceRequired;
|
||||
|
||||
int samples;
|
||||
void *handle = dlopen( "libOpenSLES.so", RTLD_LAZY );
|
||||
|
||||
if( !handle )
|
||||
return "dlopen for libOpenSLES.so";
|
||||
|
||||
pslCreateEngine = dlsym( handle, "slCreateEngine" );
|
||||
|
||||
if( !pslCreateEngine )
|
||||
return "resolve slCreateEngine";
|
||||
|
||||
pSL_IID_ENGINE = dlsym( handle, "SL_IID_ENGINE" );
|
||||
|
||||
if( !pSL_IID_ENGINE )
|
||||
return "resolve SL_IID_ENGINE";
|
||||
|
||||
pSL_IID_PLAY = dlsym( handle, "SL_IID_PLAY" );
|
||||
|
||||
if( !pSL_IID_PLAY )
|
||||
return "resolve SL_IID_PLAY";
|
||||
|
||||
pSL_IID_BUFFERQUEUE = dlsym( handle, "SL_IID_BUFFERQUEUE" );
|
||||
|
||||
if( !pSL_IID_BUFFERQUEUE )
|
||||
return "resolve SL_IID_BUFFERQUEUE";
|
||||
|
||||
|
||||
result = pslCreateEngine( &snddma_android_engine, 0, NULL, 0, NULL, NULL );
|
||||
if( result != SL_RESULT_SUCCESS ) return "slCreateEngine";
|
||||
result = (*snddma_android_engine)->Realize( snddma_android_engine, SL_BOOLEAN_FALSE );
|
||||
if( result != SL_RESULT_SUCCESS ) return "engine->Realize";
|
||||
result = (*snddma_android_engine)->GetInterface( snddma_android_engine, *pSL_IID_ENGINE, &engine );
|
||||
if( result != SL_RESULT_SUCCESS ) return "engine->GetInterface(ENGINE)";
|
||||
|
||||
result = (*engine)->CreateOutputMix( engine, &snddma_android_outputMix, 0, NULL, NULL );
|
||||
if( result != SL_RESULT_SUCCESS ) return "engine->CreateOutputMix";
|
||||
result = (*snddma_android_outputMix)->Realize( snddma_android_outputMix, SL_BOOLEAN_FALSE );
|
||||
if( result != SL_RESULT_SUCCESS ) return "outputMix->Realize";
|
||||
|
||||
freq = SOUND_DMA_SPEED;
|
||||
sourceLocator.locatorType = SL_DATALOCATOR_BUFFERQUEUE;
|
||||
sourceLocator.numBuffers = 2;
|
||||
sourceFormat.formatType = SL_DATAFORMAT_PCM;
|
||||
sourceFormat.numChannels = 2; // always stereo, because engine supports only stereo
|
||||
sourceFormat.samplesPerSec = freq * 1000;
|
||||
sourceFormat.bitsPerSample = 16; // always 16 bit audio
|
||||
sourceFormat.containerSize = sourceFormat.bitsPerSample;
|
||||
sourceFormat.channelMask = SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT;
|
||||
sourceFormat.endianness = SL_BYTEORDER_LITTLEENDIAN;
|
||||
source.pLocator = &sourceLocator;
|
||||
source.pFormat = &sourceFormat;
|
||||
|
||||
sinkLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX;
|
||||
sinkLocator.outputMix = snddma_android_outputMix;
|
||||
sink.pLocator = &sinkLocator;
|
||||
sink.pFormat = NULL;
|
||||
|
||||
interfaceID = *pSL_IID_BUFFERQUEUE;
|
||||
interfaceRequired = SL_BOOLEAN_TRUE;
|
||||
|
||||
result = (*engine)->CreateAudioPlayer( engine, &snddma_android_player, &source, &sink, 1, &interfaceID, &interfaceRequired );
|
||||
if( result != SL_RESULT_SUCCESS ) return "engine->CreateAudioPlayer";
|
||||
result = (*snddma_android_player)->Realize( snddma_android_player, SL_BOOLEAN_FALSE );
|
||||
if( result != SL_RESULT_SUCCESS ) return "player->Realize";
|
||||
result = (*snddma_android_player)->GetInterface( snddma_android_player, *pSL_IID_BUFFERQUEUE, &snddma_android_bufferQueue );
|
||||
if( result != SL_RESULT_SUCCESS ) return "player->GetInterface(BUFFERQUEUE)";
|
||||
result = (*snddma_android_player)->GetInterface( snddma_android_player, *pSL_IID_PLAY, &snddma_android_play );
|
||||
if( result != SL_RESULT_SUCCESS ) return "player->GetInterface(PLAY)";
|
||||
result = (*snddma_android_bufferQueue)->RegisterCallback( snddma_android_bufferQueue, SNDDMA_Android_Callback, NULL );
|
||||
if( result != SL_RESULT_SUCCESS ) return "bufferQueue->RegisterCallback";
|
||||
|
||||
samples = s_samplecount.value;
|
||||
if( !samples )
|
||||
samples = 4096;
|
||||
|
||||
dma.format.channels = sourceFormat.numChannels;
|
||||
dma.samples = samples * sourceFormat.numChannels;
|
||||
dma.format.speed = freq;
|
||||
snddma_android_size = dma.samples * ( sourceFormat.bitsPerSample >> 3 );
|
||||
dma.buffer = Z_Malloc( snddma_android_size * 2 );
|
||||
dma.samplepos = 0;
|
||||
// dma.sampleframes = dma.samples / dma.format.channels;
|
||||
dma.format.width = 2;
|
||||
if( !dma.buffer ) return "malloc";
|
||||
|
||||
//snddma_android_mutex = trap_Mutex_Create();
|
||||
|
||||
dma.initialized = true;
|
||||
|
||||
SNDDMA_Activate( true );
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
qboolean SNDDMA_Init( void )
|
||||
{
|
||||
const char *initError;
|
||||
|
||||
Msg( "OpenSL ES audio device initializing...\n" );
|
||||
|
||||
initError = SNDDMA_Android_Init();
|
||||
if( initError )
|
||||
{
|
||||
Msg( S_ERROR "SNDDMA_Init: %s failed.\n", initError );
|
||||
SNDDMA_Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
Msg( "OpenSL ES audio initialized.\n" );
|
||||
dma.backendName = "OpenSL ES";
|
||||
return true;
|
||||
}
|
||||
|
||||
void SNDDMA_Shutdown( void )
|
||||
{
|
||||
Msg( "Closing OpenSL ES audio device...\n" );
|
||||
|
||||
if( snddma_android_player )
|
||||
{
|
||||
(*snddma_android_player)->Destroy( snddma_android_player );
|
||||
snddma_android_player = NULL;
|
||||
}
|
||||
if( snddma_android_outputMix )
|
||||
{
|
||||
(*snddma_android_outputMix)->Destroy( snddma_android_outputMix );
|
||||
snddma_android_outputMix = NULL;
|
||||
}
|
||||
if( snddma_android_engine )
|
||||
{
|
||||
(*snddma_android_engine)->Destroy( snddma_android_engine );
|
||||
snddma_android_engine = NULL;
|
||||
}
|
||||
|
||||
if( dma.buffer )
|
||||
{
|
||||
Z_Free( dma.buffer );
|
||||
dma.buffer = NULL;
|
||||
}
|
||||
|
||||
//if( snddma_android_mutex )
|
||||
//trap_Mutex_Destroy( &snddma_android_mutex );
|
||||
|
||||
Msg( "OpenSL ES audio device shut down.\n" );
|
||||
}
|
||||
|
||||
void SNDDMA_Submit( void )
|
||||
{
|
||||
pthread_mutex_unlock( &snddma_android_mutex );
|
||||
}
|
||||
|
||||
void SNDDMA_BeginPainting( void )
|
||||
{
|
||||
pthread_mutex_lock( &snddma_android_mutex );
|
||||
}
|
||||
|
||||
qboolean VoiceCapture_Init( void )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
qboolean VoiceCapture_Activate( qboolean activate )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
qboolean VoiceCapture_Lock( qboolean lock )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void VoiceCapture_Shutdown( void )
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,513 @@
|
|||
#include "platform/platform.h"
|
||||
#if !XASH_SDL
|
||||
#include "input.h"
|
||||
#include "client.h"
|
||||
#include "filesystem.h"
|
||||
#include "platform/android/android_priv.h"
|
||||
#include "vid_common.h"
|
||||
#include <android/native_window.h>
|
||||
#include <android/native_window_jni.h>
|
||||
#include "eglutil.h"
|
||||
|
||||
|
||||
static struct vid_android_s
|
||||
{
|
||||
qboolean has_context;
|
||||
ANativeWindow* window;
|
||||
qboolean nativeegl;
|
||||
} vid_android;
|
||||
|
||||
static struct nw_s
|
||||
{
|
||||
void (*release)(ANativeWindow* window);
|
||||
int32_t (*getWidth)(ANativeWindow* window);
|
||||
int32_t (*getHeight)(ANativeWindow* window);
|
||||
int32_t (*getFormat)(ANativeWindow* window);
|
||||
int32_t (*setBuffersGeometry)(ANativeWindow* window, int32_t width, int32_t height, int32_t format);
|
||||
int32_t (*lock)(ANativeWindow* window, ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);
|
||||
int32_t (*unlockAndPost)(ANativeWindow* window);
|
||||
ANativeWindow* (*fromSurface)(JNIEnv* env, jobject surface);
|
||||
} nw;
|
||||
|
||||
#define NW_FF(x) {"ANativeWindow_"#x, (void*)&nw.x}
|
||||
|
||||
|
||||
static dllfunc_t android_funcs[] =
|
||||
{
|
||||
NW_FF(release),
|
||||
NW_FF(getWidth),
|
||||
NW_FF(getHeight),
|
||||
NW_FF(getFormat),
|
||||
NW_FF(setBuffersGeometry),
|
||||
NW_FF(lock),
|
||||
NW_FF(unlockAndPost),
|
||||
NW_FF(fromSurface),
|
||||
{ NULL, NULL }
|
||||
};
|
||||
#undef NW_FF
|
||||
static dll_info_t android_info = { "libandroid.so", android_funcs, false };
|
||||
|
||||
/*
|
||||
========================
|
||||
Android_SwapInterval
|
||||
========================
|
||||
*/
|
||||
static void Android_SwapInterval( int interval )
|
||||
{
|
||||
if( vid_android.nativeegl && eglstate.valid )
|
||||
egl.SwapInterval( eglstate.dpy, interval );
|
||||
}
|
||||
|
||||
/*
|
||||
========================
|
||||
Android_SetTitle
|
||||
========================
|
||||
*/
|
||||
static void Android_SetTitle( const char *title )
|
||||
{
|
||||
(*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.setTitle, (*jni.env)->NewStringUTF( jni.env, title ) );
|
||||
}
|
||||
|
||||
/*
|
||||
========================
|
||||
Android_SetIcon
|
||||
========================
|
||||
*/
|
||||
static void Android_SetIcon( const char *path )
|
||||
{
|
||||
(*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.setIcon, (*jni.env)->NewStringUTF( jni.env, path ) );
|
||||
}
|
||||
|
||||
/*
|
||||
========================
|
||||
Android_GetScreenRes
|
||||
|
||||
Resolution got from last resize event
|
||||
========================
|
||||
*/
|
||||
static void Android_GetScreenRes( int *width, int *height )
|
||||
{
|
||||
*width = jni.width;
|
||||
*height = jni.height;
|
||||
}
|
||||
|
||||
/*
|
||||
========================
|
||||
Android_SwapBuffers
|
||||
|
||||
Update screen. Use native EGL if possible
|
||||
========================
|
||||
*/
|
||||
void GL_SwapBuffers( void )
|
||||
{
|
||||
if( vid_android.nativeegl && eglstate.valid )
|
||||
{
|
||||
egl.SwapBuffers( eglstate.dpy, eglstate.surface );
|
||||
}
|
||||
else
|
||||
{
|
||||
(*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.swapBuffers );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
========================
|
||||
Android_UpdateSurface
|
||||
|
||||
Check if we may use native EGL without jni calls
|
||||
========================
|
||||
*/
|
||||
void Android_UpdateSurface( surfacestate_t state )
|
||||
{
|
||||
qboolean active = state == surface_active;
|
||||
vid_android.nativeegl = false;
|
||||
|
||||
if( glw_state.software || ( eglstate.valid && !eglstate.imported ))
|
||||
{
|
||||
if( vid_android.window )
|
||||
{
|
||||
EGL_UpdateSurface( NULL, false );
|
||||
nw.release( vid_android.window );
|
||||
vid_android.window = NULL;
|
||||
}
|
||||
if( state == surface_dummy && glw_state.software )
|
||||
return;
|
||||
// first, ask EGL for surfaceless mode
|
||||
if( state == surface_dummy && EGL_UpdateSurface( NULL, true ))
|
||||
{
|
||||
vid_android.nativeegl = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if( state != surface_pause )
|
||||
{
|
||||
EGLint format = WINDOW_FORMAT_RGB_565;
|
||||
jobject surf;
|
||||
if( vid_android.window )
|
||||
nw.release( vid_android.window );
|
||||
surf = (*jni.env)->CallStaticObjectMethod( jni.env, jni.bindcls, jni.getSurface, state );
|
||||
Con_DPrintf("Surface handle %p\n", surf);
|
||||
if( surf )
|
||||
{
|
||||
vid_android.window = nw.fromSurface(jni.env, surf);
|
||||
Con_DPrintf("NativeWindow %p\n", vid_android.window);
|
||||
|
||||
if( eglstate.valid )
|
||||
egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_NATIVE_VISUAL_ID, &format );
|
||||
|
||||
nw.setBuffersGeometry(vid_android.window, 0, 0, format );
|
||||
|
||||
(*jni.env)->DeleteLocalRef( jni.env, surf );
|
||||
}
|
||||
}
|
||||
|
||||
if( eglstate.valid && !eglstate.imported )
|
||||
{
|
||||
EGL_UpdateSurface( vid_android.window, state == surface_dummy );
|
||||
vid_android.nativeegl = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if( !vid_android.has_context )
|
||||
return;
|
||||
|
||||
(*jni.env)->CallStaticVoidMethod( jni.env, jni.bindcls, jni.toggleEGL, (int)state );
|
||||
host.status = HOST_FRAME; // active ? HOST_FRAME : HOST_SLEEP;
|
||||
|
||||
// todo: check opengl context here and set HOST_SLEEP if not
|
||||
|
||||
if( !Sys_CheckParm("-nativeegl") || !active )
|
||||
return; // enabled by user
|
||||
|
||||
vid_android.nativeegl = EGL_ImportContext();
|
||||
if( vid_android.nativeegl )
|
||||
Con_DPrintf( "nativeEGL success\n");
|
||||
}
|
||||
|
||||
/*
|
||||
========================
|
||||
Android_GetGLAttribute
|
||||
========================
|
||||
*/
|
||||
static int Android_GetGLAttribute( int eglAttr )
|
||||
{
|
||||
int ret = (*jni.env)->CallStaticIntMethod( jni.env, jni.bindcls, jni.getGLAttribute, eglAttr );
|
||||
// Con_Reportf( "Android_GetGLAttribute( %i ) => %i\n", eglAttr, ret );
|
||||
return ret;
|
||||
}
|
||||
|
||||
qboolean R_Init_Video( const int type )
|
||||
{
|
||||
qboolean retval;
|
||||
char buf[10]; // Sys_GetParmFromCmdLine
|
||||
|
||||
if( FS_FileExists( GI->iconpath, true ) )
|
||||
{
|
||||
// TODO: convert icon to some android-readable format and place
|
||||
Android_SetIcon( FS_GetDiskPath( GI->iconpath, false ));
|
||||
}
|
||||
|
||||
Android_SetTitle( GI->title );
|
||||
|
||||
VID_StartupGamma();
|
||||
|
||||
Sys_LoadLibrary( &android_info );
|
||||
|
||||
switch( type )
|
||||
{
|
||||
case REF_SOFTWARE:
|
||||
glw_state.software = true;
|
||||
break;
|
||||
case REF_GL:
|
||||
glw_state.software = false;
|
||||
EGL_LoadLibrary();
|
||||
|
||||
if( !glw_state.safe && Sys_GetParmFromCmdLine( "-safegl", buf ) )
|
||||
glw_state.safe = bound( SAFE_NO, Q_atoi( buf ), SAFE_DONTCARE );
|
||||
|
||||
break;
|
||||
default:
|
||||
Host_Error( "Can't initialize unknown context type %d!\n", type );
|
||||
break;
|
||||
}
|
||||
|
||||
if( glw_state.software )
|
||||
{
|
||||
uint arg;
|
||||
|
||||
if( !nw.release )
|
||||
{
|
||||
Con_Reportf( S_ERROR "Native software mode unavailiable\n" );
|
||||
return false;
|
||||
}
|
||||
Android_UpdateSurface( surface_active );
|
||||
if( !SW_CreateBuffer( jni.width, jni.height, &arg, &arg, &arg, &arg, &arg ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
while( !(retval = VID_SetMode()) )
|
||||
{
|
||||
glw_state.safe++;
|
||||
if( glw_state.safe > SAFE_LAST )
|
||||
return false;
|
||||
}
|
||||
|
||||
switch( type )
|
||||
{
|
||||
case REF_GL:
|
||||
// refdll also can check extensions
|
||||
ref.dllFuncs.GL_InitExtensions();
|
||||
break;
|
||||
case REF_SOFTWARE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
host.renderinfo_changed = false;
|
||||
|
||||
host.status = HOST_FRAME; // where it should we done? We have broken host.status on all non-SDL platforms!
|
||||
return true;
|
||||
}
|
||||
|
||||
void R_Free_Video( void )
|
||||
{
|
||||
// (*jni.env)->CallStaticBooleanMethod( jni.env, jni.bindcls, jni.deleteGLContext );
|
||||
|
||||
// VID_DestroyWindow ();
|
||||
|
||||
// R_FreeVideoModes();
|
||||
Sys_FreeLibrary( &android_info );
|
||||
vid_android.has_context = false;
|
||||
ref.dllFuncs.GL_ClearExtensions();
|
||||
}
|
||||
|
||||
void *Android_GetWindow( void )
|
||||
{
|
||||
EGLint format;
|
||||
|
||||
if( !vid_android.window )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( !egl.GetConfigAttrib( eglstate.dpy, eglstate.cfg, EGL_NATIVE_VISUAL_ID, &format) )
|
||||
{
|
||||
Con_Reportf( S_ERROR "eglGetConfigAttrib(VISUAL_ID) returned error: 0x%x\n", egl.GetError() );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( nw.setBuffersGeometry( vid_android.window, 0, 0, format ) )
|
||||
{
|
||||
Con_Reportf( S_ERROR "ANativeWindow_setBuffersGeometry returned error\n" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return vid_android.window;
|
||||
}
|
||||
|
||||
|
||||
qboolean VID_SetMode( void )
|
||||
{
|
||||
EGLint format;
|
||||
jintArray attribs, contextAttribs;
|
||||
static EGLint nAttribs[32+1], nContextAttribs[32+1];
|
||||
const size_t attribsSize = ARRAYSIZE( nAttribs );
|
||||
size_t s1, s2;
|
||||
|
||||
// create context on egl side by user request
|
||||
if( !vid_android.has_context && Sys_CheckParm("-egl") )
|
||||
{
|
||||
vid_android.has_context = vid_android.nativeegl = EGL_CreateContext();
|
||||
|
||||
if( vid_android.has_context )
|
||||
Android_UpdateSurface( surface_active );
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
if( vid_android.has_context || glw_state.software )
|
||||
{
|
||||
R_ChangeDisplaySettings( 0, 0, WINDOW_MODE_WINDOWED ); // width and height are ignored anyway
|
||||
return true;
|
||||
}
|
||||
|
||||
s1 = EGL_GenerateConfig(nAttribs, attribsSize);
|
||||
s2 = EGL_GenerateContextConfig(nContextAttribs, attribsSize);
|
||||
|
||||
attribs = (*jni.env)->NewIntArray( jni.env, s1 );
|
||||
contextAttribs = (*jni.env)->NewIntArray( jni.env, s2 );
|
||||
|
||||
(*jni.env)->SetIntArrayRegion( jni.env, attribs, 0, s1, nAttribs );
|
||||
(*jni.env)->SetIntArrayRegion( jni.env, contextAttribs, 0, s2, nContextAttribs );
|
||||
|
||||
R_ChangeDisplaySettings( 0, 0, WINDOW_MODE_WINDOWED ); // width and height are ignored anyway
|
||||
|
||||
|
||||
if( (*jni.env)->CallStaticBooleanMethod( jni.env, jni.bindcls, jni.createGLContext, attribs, contextAttribs ) )
|
||||
{
|
||||
vid_android.has_context = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
rserr_t R_ChangeDisplaySettings( int width, int height, window_mode_t window_mode )
|
||||
{
|
||||
int render_w, render_h;
|
||||
|
||||
Android_GetScreenRes(&width, &height);
|
||||
|
||||
render_w = width;
|
||||
render_h = height;
|
||||
|
||||
Con_Reportf( "R_ChangeDisplaySettings: forced resolution to %dx%d)\n", width, height);
|
||||
|
||||
VID_SetDisplayTransform( &render_w, &render_h );
|
||||
|
||||
R_SaveVideoMode( width, height, render_w, render_h, true );
|
||||
|
||||
refState.wideScreen = true; // V_AdjustFov will check for widescreen
|
||||
|
||||
return rserr_ok;
|
||||
}
|
||||
|
||||
int GL_SetAttribute( int attr, int val )
|
||||
{
|
||||
return EGL_SetAttribute( attr, val );
|
||||
}
|
||||
|
||||
int GL_GetAttribute( int attr, int *val )
|
||||
{
|
||||
EGLBoolean ret;
|
||||
|
||||
if( eglstate.valid )
|
||||
return EGL_GetAttribute( attr, val );
|
||||
|
||||
if( attr < 0 || attr >= REF_GL_ATTRIBUTES_COUNT )
|
||||
return -1;
|
||||
|
||||
if( !val )
|
||||
return -1;
|
||||
|
||||
switch( attr )
|
||||
{
|
||||
case REF_GL_RED_SIZE:
|
||||
*val = Android_GetGLAttribute( EGL_RED_SIZE );
|
||||
return 0;
|
||||
case REF_GL_GREEN_SIZE:
|
||||
*val = Android_GetGLAttribute( EGL_GREEN_SIZE );
|
||||
return 0;
|
||||
case REF_GL_BLUE_SIZE:
|
||||
*val = Android_GetGLAttribute( EGL_BLUE_SIZE );
|
||||
return 0;
|
||||
case REF_GL_ALPHA_SIZE:
|
||||
*val = Android_GetGLAttribute( EGL_ALPHA_SIZE );
|
||||
return 0;
|
||||
case REF_GL_DEPTH_SIZE:
|
||||
*val = Android_GetGLAttribute( EGL_DEPTH_SIZE );
|
||||
return 0;
|
||||
case REF_GL_STENCIL_SIZE:
|
||||
*val = Android_GetGLAttribute( EGL_STENCIL_SIZE );
|
||||
return 0;
|
||||
case REF_GL_MULTISAMPLESAMPLES:
|
||||
*val = Android_GetGLAttribute( EGL_SAMPLES );
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int R_MaxVideoModes( void )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
vidmode_t* R_GetVideoMode( int num )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* GL_GetProcAddress( const char *name ) // RenderAPI requirement
|
||||
{
|
||||
return EGL_GetProcAddress( name );
|
||||
}
|
||||
|
||||
void GL_UpdateSwapInterval( void )
|
||||
{
|
||||
// disable VSync while level is loading
|
||||
if( cls.state < ca_active )
|
||||
{
|
||||
Android_SwapInterval( 0 );
|
||||
SetBits( gl_vsync.flags, FCVAR_CHANGED );
|
||||
}
|
||||
else if( FBitSet( gl_vsync.flags, FCVAR_CHANGED ))
|
||||
{
|
||||
ClearBits( gl_vsync.flags, FCVAR_CHANGED );
|
||||
Android_SwapInterval( gl_vsync.value );
|
||||
}
|
||||
}
|
||||
|
||||
void *SW_LockBuffer( void )
|
||||
{
|
||||
ANativeWindow_Buffer buffer;
|
||||
if( !nw.lock || !vid_android.window )
|
||||
return NULL;
|
||||
if( nw.lock( vid_android.window, &buffer, NULL ) )
|
||||
return NULL;
|
||||
if( buffer.width < refState.width || buffer.height < refState.height )
|
||||
return NULL;
|
||||
return buffer.bits;
|
||||
}
|
||||
|
||||
void SW_UnlockBuffer( void )
|
||||
{
|
||||
if( nw.unlockAndPost )
|
||||
nw.unlockAndPost( vid_android.window );
|
||||
}
|
||||
|
||||
qboolean SW_CreateBuffer( int width, int height, uint *stride, uint *bpp, uint *r, uint *g, uint *b )
|
||||
{
|
||||
ANativeWindow_Buffer buffer;
|
||||
int lock;
|
||||
if( !nw.lock || !vid_android.window )
|
||||
return false;
|
||||
nw.unlockAndPost( vid_android.window );
|
||||
|
||||
if( ( lock = nw.lock( vid_android.window, &buffer, NULL ) ) )
|
||||
{
|
||||
Con_Printf( "SW_CreateBuffer: lock %d\n", lock );
|
||||
return false;
|
||||
}
|
||||
nw.unlockAndPost( vid_android.window );
|
||||
Con_Printf( "SW_CreateBuffer: buffer %d %d %x %d %p\n", buffer.width, buffer.height, buffer.format, buffer.stride, buffer.bits );
|
||||
if( width > buffer.width || height > buffer.height )
|
||||
{
|
||||
// resize event missed? do not resize now, wait for REAL resize event or when java code will be fixed
|
||||
Con_Printf( S_ERROR "SW_CreateBuffer: buffer too small, need %dx%d, got %dx%d, java part probably sucks\n", width, height, buffer.width, buffer.height );
|
||||
#if 0
|
||||
if( jni.width < buffer.width )
|
||||
jni.width = buffer.width;
|
||||
if( jni.height < buffer.height )
|
||||
jni.width = buffer.height;
|
||||
VID_SetMode();
|
||||
Android_UpdateSurface( true );
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
if( buffer.format != WINDOW_FORMAT_RGB_565 )
|
||||
{
|
||||
Con_Printf( "SW_CreateBuffer: wrong format %d\n", buffer.format );
|
||||
return false;
|
||||
}
|
||||
Con_Printf( "SW_CreateBuffer: ok\n" );
|
||||
*stride = buffer.stride;
|
||||
|
||||
*bpp = 2;
|
||||
*r = (((1 << 5) - 1) << (5+6));
|
||||
*g = (((1 << 6) - 1) << (5));
|
||||
*b = (((1 << 5) - 1) << (0));
|
||||
return true;
|
||||
}
|
||||
#endif
|
|
@ -139,6 +139,8 @@ static inline void Platform_Shutdown( void )
|
|||
|
||||
#if XASH_SDL
|
||||
SDLash_Shutdown( );
|
||||
#elif XASH_ANDROID
|
||||
Android_Shutdown();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,9 @@ def options(opt):
|
|||
grp.add_option('--enable-engine-fuzz', action = 'store_true', dest = 'ENGINE_FUZZ', default = False,
|
||||
help = 'add LLVM libFuzzer [default: %default]' )
|
||||
|
||||
grp.add_option('--enable-android-legacy', action = 'store_true', dest = 'ANDROID_LEGACY', default = False,
|
||||
help = 'allow build legacy android port without SDL (deprecated, need for renderers debug on ancient devices)')
|
||||
|
||||
opt.load('sdl2')
|
||||
|
||||
def configure(conf):
|
||||
|
@ -75,6 +78,8 @@ def configure(conf):
|
|||
conf.define('XASH_SDL', 12)
|
||||
conf.check_cfg(package='sdl', args='--cflags --libs', uselib_store='SDL2' )
|
||||
conf.env.HAVE_SDL2 = True
|
||||
elif conf.options.ANDROID_LEGACY:
|
||||
pass #just test if it can build
|
||||
else:
|
||||
conf.load('sdl2')
|
||||
if not conf.env.HAVE_SDL2:
|
||||
|
|
|
@ -556,54 +556,70 @@ qboolean GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char
|
|||
for( func = funcs; func && func->name; func++ )
|
||||
{
|
||||
// functions are cleared before all the extensions are evaluated
|
||||
if(( *func->func = (void *)gEngfuncs.GL_GetProcAddress( func->name )) == NULL )
|
||||
{
|
||||
string name;
|
||||
char *end;
|
||||
size_t i = 0;
|
||||
string name;
|
||||
char *end;
|
||||
size_t i = 0;
|
||||
qboolean first_try = true;
|
||||
// NULL means just func->name, but empty suffix cuts ARB suffix if present
|
||||
#ifdef XASH_GLES
|
||||
const char *suffixes[] = { "", "EXT", "OES" };
|
||||
const char *suffixes[] = { "", "EXT", "OES", NULL, "ARB" };
|
||||
#else
|
||||
const char *suffixes[] = { "", "EXT" };
|
||||
const char *suffixes[] = { NULL, "", "EXT", "ARB" };
|
||||
#endif
|
||||
|
||||
// HACK: fix ARB names
|
||||
Q_strncpy( name, func->name, sizeof( name ));
|
||||
if(( end = Q_strstr( name, "ARB" )))
|
||||
{
|
||||
*end = '\0';
|
||||
}
|
||||
else // I need Q_strstrnul
|
||||
{
|
||||
end = name + Q_strlen( name );
|
||||
i++; // skip empty suffix
|
||||
}
|
||||
// Remove ARB suffix
|
||||
Q_strncpy( name, func->name, sizeof( name ));
|
||||
if(( end = Q_strstr( name, "ARB" )))
|
||||
{
|
||||
*end = '\0';
|
||||
}
|
||||
else // I need Q_strstrnul
|
||||
{
|
||||
end = name + Q_strlen( name );
|
||||
}
|
||||
|
||||
for( ; i < sizeof( suffixes ) / sizeof( suffixes[0] ); i++ )
|
||||
{
|
||||
void *f;
|
||||
for( ; i < sizeof( suffixes ) / sizeof( suffixes[0] ); i++ )
|
||||
{
|
||||
void *f;
|
||||
const char *pname = name;
|
||||
size_t name_len = end - pname;
|
||||
const char *orig_suffix = func->name + name_len;
|
||||
|
||||
if( suffixes[i] )
|
||||
{
|
||||
// if suffix is original suffix, it's handled with NULL
|
||||
if( orig_suffix[0] && !Q_strcmp( orig_suffix, suffixes[i] ))
|
||||
continue;
|
||||
Q_strncat( name, suffixes[i], sizeof( name ));
|
||||
|
||||
if(( f = gEngfuncs.GL_GetProcAddress( name )))
|
||||
{
|
||||
// GL_GetProcAddress prints errors about missing functions, so tell user that we found it with different name
|
||||
gEngfuncs.Con_Printf( S_NOTE "found %s\n", name );
|
||||
|
||||
*func->func = f;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
*end = '\0'; // cut suffix, try next
|
||||
}
|
||||
}
|
||||
|
||||
// not found...
|
||||
if( i == sizeof( suffixes ) / sizeof( suffixes[0] ))
|
||||
else
|
||||
{
|
||||
GL_SetExtension( r_ext, false );
|
||||
// if original name does not have a suffix, it will be handled with empty suffix
|
||||
if( !orig_suffix[0] )
|
||||
continue;
|
||||
pname = func->name;
|
||||
}
|
||||
|
||||
if(( f = gEngfuncs.GL_GetProcAddress( pname )))
|
||||
{
|
||||
// if we already tried this function, notify about success after previous error from GL_GetProcAddress
|
||||
if(!first_try)
|
||||
gEngfuncs.Con_Printf( S_NOTE "found %s\n", pname );
|
||||
first_try = false;
|
||||
|
||||
*func->func = f;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
*end = '\0'; // cut suffix, try next
|
||||
}
|
||||
}
|
||||
|
||||
// not found...
|
||||
if( i == sizeof( suffixes ) / sizeof( suffixes[0] ))
|
||||
{
|
||||
GL_SetExtension( r_ext, false );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -44,6 +44,7 @@ class Android:
|
|||
ndk_rev = 0
|
||||
is_hardfloat = False
|
||||
clang = False
|
||||
ndk_binutils = False
|
||||
|
||||
def __init__(self, ctx, arch, toolchain, api):
|
||||
self.ctx = ctx
|
||||
|
@ -214,7 +215,7 @@ class Android:
|
|||
if 'CC' in environ:
|
||||
s = environ['CC']
|
||||
|
||||
return '%s --target=%s%d' % (s, self.ndk_triplet(), self.api)
|
||||
return '%s --target=%s' % (s, self.ndk_triplet()) #%s%d' % (s, self.ndk_triplet(), self.api)
|
||||
return self.gen_toolchain_path() + ('clang' if self.is_clang() else 'gcc')
|
||||
|
||||
def cxx(self):
|
||||
|
@ -225,11 +226,11 @@ class Android:
|
|||
if 'CXX' in environ:
|
||||
s = environ['CXX']
|
||||
|
||||
return '%s --target=%s%d' % (s, self.ndk_triplet(), self.api)
|
||||
return '%s --target=%s' % (s, self.ndk_triplet()) #%s%d' % (s, self.ndk_triplet(), self.api)
|
||||
return self.gen_toolchain_path() + ('clang++' if self.is_clang() else 'g++')
|
||||
|
||||
def strip(self):
|
||||
if self.is_host():
|
||||
if self.is_host() and not self.ndk_binutils:
|
||||
environ = getattr(self.ctx, 'environ', os.environ)
|
||||
|
||||
if 'STRIP' in environ:
|
||||
|
@ -240,9 +241,22 @@ class Android:
|
|||
return os.path.join(self.gen_binutils_path(), 'llvm-strip')
|
||||
return os.path.join(self.gen_binutils_path(), 'strip')
|
||||
|
||||
def objcopy(self):
|
||||
if self.is_host() and not self.ndk_binutils:
|
||||
environ = getattr(self.ctx, 'environ', os.environ)
|
||||
|
||||
if 'OBJCOPY' in environ:
|
||||
return environ['OBJCOPY']
|
||||
return 'llvm-objcopy'
|
||||
|
||||
if self.ndk_rev >= 23:
|
||||
return os.path.join(self.gen_binutils_path(), 'llvm-objcopy')
|
||||
return os.path.join(self.gen_binutils_path(), 'objcopy')
|
||||
|
||||
def system_stl(self):
|
||||
# TODO: proper STL support
|
||||
return os.path.abspath(os.path.join(self.ndk_home, 'sources', 'cxx-stl', 'system', 'include'))
|
||||
#return os.path.abspath(os.path.join(self.ndk_home, 'sources', 'cxx-stl', 'system', 'include')) broken with clang-15? TODO: check different ndk versions
|
||||
return os.path.abspath(os.path.join(self.ndk_home, 'sources', 'cxx-stl', 'stlport', 'stlport'))
|
||||
|
||||
def libsysroot(self):
|
||||
arch = self.arch
|
||||
|
@ -262,8 +276,15 @@ class Android:
|
|||
|
||||
def cflags(self, cxx = False):
|
||||
cflags = []
|
||||
android_from_none = False
|
||||
if self.is_host() and self.is_arm() and self.is_hardfp():
|
||||
# clang android target may change with ndk
|
||||
# override target to none while compiling and
|
||||
# add some android options manually
|
||||
android_from_none = True
|
||||
cflags += ['--target=arm-none-eabi']
|
||||
|
||||
if self.ndk_rev <= ANDROID_NDK_SYSROOT_FLAG_MAX:
|
||||
if self.ndk_rev <= ANDROID_NDK_SYSROOT_FLAG_MAX and not android_from_none:
|
||||
cflags += ['--sysroot=%s' % (self.sysroot())]
|
||||
else:
|
||||
if self.is_host():
|
||||
|
@ -273,13 +294,18 @@ class Android:
|
|||
]
|
||||
|
||||
cflags += ['-I%s' % (self.system_stl())]
|
||||
if not self.is_clang():
|
||||
cflags += ['-DANDROID', '-D__ANDROID__']
|
||||
if not self.is_clang() or android_from_none:
|
||||
cflags += ['-DANDROID', '-D__ANDROID__=1']
|
||||
if android_from_none:
|
||||
cflags += [ '-D__linux__=1', '-fPIC'] # TODO: compare with linux target?
|
||||
|
||||
if cxx and not self.is_clang() and self.toolchain not in ['4.8','4.9']:
|
||||
cflags += ['-fno-sized-deallocation']
|
||||
|
||||
if self.is_clang():
|
||||
if cxx and (self.is_clang() or self.is_host()):
|
||||
cflags += [ '-Wno-dynamic-exception-spec', '-fno-rtti' ]
|
||||
|
||||
if self.is_clang() or self.is_host():
|
||||
# stpcpy() isn't available in early Android versions
|
||||
# disable it here so Clang won't use it
|
||||
if self.api < ANDROID_STPCPY_API_MIN:
|
||||
|
@ -291,7 +317,7 @@ class Android:
|
|||
cflags += ['-mthumb', '-mfpu=neon', '-mcpu=cortex-a9']
|
||||
|
||||
if self.is_hardfp():
|
||||
cflags += ['-D_NDK_MATH_NO_SOFTFP=1', '-mfloat-abi=hard', '-DLOAD_HARDFP', '-DSOFTFP_LINK']
|
||||
cflags += ['-D_NDK_MATH_NO_SOFTFP=1', '-mfloat-abi=hard', '-DLOAD_HARDFP', '-DSOFTFP_LINK', '-DGLES_SOFTFLOAT']
|
||||
|
||||
if self.is_host():
|
||||
# Clang builtin redefine w/ different calling convention bug
|
||||
|
@ -316,15 +342,23 @@ class Android:
|
|||
linkflags = []
|
||||
if self.is_host():
|
||||
linkflags += ['--gcc-toolchain=%s' % self.gen_gcc_toolchain_path()]
|
||||
linkflags += ['--gcc-install-dir=%s/lib/gcc/%s/4.9/' % (self.gen_gcc_toolchain_path(), self.ndk_triplet())]
|
||||
if self.ndk_binutils:
|
||||
linkflags += ['-fuse-ld=%s/bin/%s-ld.bfd' % (self.gen_gcc_toolchain_path(), self.ndk_triplet())]
|
||||
else:
|
||||
linkflags += ['-fuse-ld=lld']
|
||||
linkflags += ['--unwindlib=none']
|
||||
linkflags += ['--rtlib=libgcc']
|
||||
|
||||
if self.ndk_rev <= ANDROID_NDK_SYSROOT_FLAG_MAX:
|
||||
linkflags += ['--sysroot=%s' % (self.sysroot())]
|
||||
elif self.is_host():
|
||||
linkflags += ['--sysroot=%s/sysroot' % (self.gen_gcc_toolchain_path())]
|
||||
|
||||
if self.is_clang() or self.is_host():
|
||||
linkflags += ['-fuse-ld=lld']
|
||||
else: linkflags += ['-no-canonical-prefixes']
|
||||
#if self.is_clang() or self.is_host():
|
||||
# linkflags += ['-fuse-ld=lld']
|
||||
#else:
|
||||
linkflags += ['-no-canonical-prefixes']
|
||||
|
||||
linkflags += ['-Wl,--hash-style=sysv', '-Wl,--no-undefined']
|
||||
return linkflags
|
||||
|
@ -528,6 +562,7 @@ def configure(conf):
|
|||
conf.environ['CC'] = android.cc()
|
||||
conf.environ['CXX'] = android.cxx()
|
||||
conf.environ['STRIP'] = android.strip()
|
||||
conf.environ['OBJCOPY'] = android.objcopy()
|
||||
conf.env.CFLAGS += android.cflags()
|
||||
conf.env.CXXFLAGS += android.cflags(True)
|
||||
conf.env.LINKFLAGS += android.linkflags()
|
||||
|
|
|
@ -44,16 +44,16 @@ def create_zip_archive(self):
|
|||
self.path.get_bld().mkdir()
|
||||
target = self.path.get_bld().make_node(self.name)
|
||||
|
||||
tsk = self.create_task('ziparchive', files, target)
|
||||
self.zip_task = self.create_task('ziparchive', files, target)
|
||||
|
||||
setattr(tsk, 'compresslevel', compresslevel)
|
||||
setattr(tsk, 'relative_to', relative_to)
|
||||
setattr(self.zip_task, 'compresslevel', compresslevel)
|
||||
setattr(self.zip_task, 'relative_to', relative_to)
|
||||
|
||||
try:
|
||||
inst_to = self.install_path
|
||||
self.install_task = self.add_install_files(
|
||||
install_to=inst_to, install_from=target,
|
||||
chmod=Utils.O644, task=tsk)
|
||||
chmod=Utils.O644, task=self.zip_task)
|
||||
|
||||
except AttributeError:
|
||||
pass
|
||||
|
|
48
wscript
48
wscript
|
@ -2,7 +2,7 @@
|
|||
# encoding: utf-8
|
||||
# a1batross, mittorn, 2018
|
||||
|
||||
from waflib import Build, Context, Logs
|
||||
from waflib import Build, Context, Logs, Options, Configure
|
||||
from waflib.Tools import waf_unit_test
|
||||
import sys
|
||||
import os
|
||||
|
@ -69,7 +69,7 @@ SUBDIRS = [
|
|||
Subproject('dllemu'),
|
||||
|
||||
# disable only by engine feature, makes no sense to even parse subprojects in dedicated mode
|
||||
Subproject('3rdparty/extras', lambda x: not x.env.DEDICATED and x.env.DEST_OS != 'android'),
|
||||
Subproject('3rdparty/extras', lambda x: not x.env.DEDICATED),
|
||||
Subproject('3rdparty/nanogl', lambda x: not x.env.DEDICATED and x.env.NANOGL),
|
||||
Subproject('3rdparty/gl-wes-v2', lambda x: not x.env.DEDICATED and x.env.GLWES),
|
||||
Subproject('3rdparty/gl4es', lambda x: not x.env.DEDICATED and x.env.GL4ES),
|
||||
|
@ -104,6 +104,42 @@ REFDLLS = [
|
|||
RefDll('null', False),
|
||||
]
|
||||
|
||||
def process_extra_projects_opts(ctx):
|
||||
options, commands, envvars = ctx.parse_cmd_args(allow_unknown = True)
|
||||
projs = options.EXTRA_PROJECTS
|
||||
if not projs:
|
||||
return
|
||||
for proj in projs.split(','):
|
||||
ctx.add_subproject(proj)
|
||||
def process_extra_projects_conf(ctx):
|
||||
projs = ctx.options.EXTRA_PROJECTS
|
||||
if not projs:
|
||||
return
|
||||
ctx.env.EXTRA_PROJECTS = projs
|
||||
|
||||
for proj in projs.split(','):
|
||||
tools_orig = ctx.tools.copy()
|
||||
ctx.add_subproject(proj)
|
||||
waifulib_path = os.path.join(proj, 'scripts', 'waifulib')
|
||||
if os.path.isdir(waifulib_path):
|
||||
for tool in ctx.tools:
|
||||
if not tool in tools_orig:
|
||||
if os.path.isfile(os.path.join(waifulib_path, tool['tool'] + '.py')):
|
||||
tool['tooldir'] = [waifulib_path]
|
||||
Logs.info('External tooldir set: ' + str(tool))
|
||||
|
||||
|
||||
def process_extra_projects_bld(ctx):
|
||||
projs = ctx.env.EXTRA_PROJECTS
|
||||
if not projs:
|
||||
return
|
||||
for proj in projs.split(','):
|
||||
ctx.add_subproject(proj)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def options(opt):
|
||||
opt.load('reconfigure compiler_optimizations xshlib xcompile compiler_cxx compiler_c sdl2 clang_compilation_database strip_on_install waf_unit_test msdev msvs msvc subproject cmake')
|
||||
|
||||
|
@ -154,12 +190,15 @@ def options(opt):
|
|||
|
||||
grp.add_option('--enable-fuzzer', action = 'store_true', dest = 'ENABLE_FUZZER', default = False,
|
||||
help = 'enable building libFuzzer runner [default: %default]' )
|
||||
grp.add_option('--extra-projects', action = 'store', dest = 'EXTRA_PROJECTS', default = '', type = 'string',
|
||||
help = 'add extra projects' )
|
||||
|
||||
for i in SUBDIRS:
|
||||
if not i.is_exists(opt):
|
||||
continue
|
||||
|
||||
opt.add_subproject(i.name)
|
||||
process_extra_projects_opts(opt)
|
||||
|
||||
def configure(conf):
|
||||
conf.load('fwgslib reconfigure compiler_optimizations')
|
||||
|
@ -201,7 +240,8 @@ def configure(conf):
|
|||
conf.options.GL4ES = True
|
||||
conf.options.GLES3COMPAT = True
|
||||
conf.options.GL = False
|
||||
conf.define('XASH_SDLMAIN', 1)
|
||||
if conf.env.HAVE_SDL2:
|
||||
conf.define('XASH_SDLMAIN', 1)
|
||||
elif conf.env.MAGX:
|
||||
conf.options.SDL12 = True
|
||||
conf.options.NO_VGUI = True
|
||||
|
@ -498,6 +538,7 @@ int main(void) { return !opus_custom_encoder_init((OpusCustomEncoder *)1, (const
|
|||
continue
|
||||
|
||||
conf.add_subproject(i.name)
|
||||
process_extra_projects_conf(conf)
|
||||
|
||||
def build(bld):
|
||||
# guard rails to not let install to root
|
||||
|
@ -520,3 +561,4 @@ def build(bld):
|
|||
if bld.env.TESTS:
|
||||
bld.add_post_fun(waf_unit_test.summary)
|
||||
bld.add_post_fun(waf_unit_test.set_exit_code)
|
||||
process_extra_projects_bld(bld)
|
||||
|
|
Loading…
Reference in New Issue