Merge pull request #930 from PatrickvL/nv2a_work
NV2A work enabling more OpenGL code
This commit is contained in:
commit
a9f2f78b04
|
@ -264,8 +264,16 @@
|
|||
<ClInclude Include="..\..\src\devices\SMBus.h" />
|
||||
<ClInclude Include="..\..\src\devices\SMCDevice.h" />
|
||||
<ClInclude Include="..\..\src\devices\SMDevice.h" />
|
||||
<ClInclude Include="..\..\src\devices\video\glextensions.h" />
|
||||
<ClInclude Include="..\..\src\devices\video\gloffscreen.h" />
|
||||
<ClInclude Include="..\..\src\devices\video\nv2a.h" />
|
||||
<ClInclude Include="..\..\src\devices\video\nv2a_debug.h" />
|
||||
<ClInclude Include="..\..\src\devices\video\nv2a_int.h" />
|
||||
<ClInclude Include="..\..\src\devices\video\nv2a_psh.h" />
|
||||
<ClInclude Include="..\..\src\devices\video\nv2a_shaders.h" />
|
||||
<ClInclude Include="..\..\src\devices\video\nv2a_shaders_common.h" />
|
||||
<ClInclude Include="..\..\src\devices\video\nv2a_vsh.h" />
|
||||
<ClInclude Include="..\..\src\devices\video\queue.h" />
|
||||
<ClInclude Include="..\..\src\devices\video\swizzle.h" />
|
||||
<ClInclude Include="..\..\src\devices\video\vga.h" />
|
||||
<ClInclude Include="..\..\src\devices\Xbox.h" />
|
||||
|
@ -632,7 +640,13 @@
|
|||
<ClCompile Include="..\..\src\devices\SMBus.cpp" />
|
||||
<ClCompile Include="..\..\src\devices\SMCDevice.cpp" />
|
||||
<ClCompile Include="..\..\src\devices\SMDevice.cpp" />
|
||||
<ClCompile Include="..\..\src\devices\video\glextensions.c" />
|
||||
<ClCompile Include="..\..\src\devices\video\gloffscreen.c" />
|
||||
<ClCompile Include="..\..\src\devices\video\nv2a.cpp" />
|
||||
<ClCompile Include="..\..\src\devices\video\nv2a_debug.c" />
|
||||
<ClCompile Include="..\..\src\devices\video\nv2a_psh.cpp" />
|
||||
<ClCompile Include="..\..\src\devices\video\nv2a_shaders.cpp" />
|
||||
<ClCompile Include="..\..\src\devices\video\nv2a_vsh.cpp" />
|
||||
<ClCompile Include="..\..\src\devices\video\swizzle.cpp" />
|
||||
<ClCompile Include="..\..\src\devices\Xbox.cpp" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -217,18 +217,12 @@
|
|||
<ClCompile Include="..\..\src\devices\SMDevice.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\devices\video\nv2a.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\Common\CxbxDebugger.cpp">
|
||||
<Filter>Shared</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\devices\Xbox.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\devices\EmuNVNet.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\Common\XbePrinter.cpp">
|
||||
<Filter>Shared</Filter>
|
||||
</ClCompile>
|
||||
|
@ -241,12 +235,36 @@
|
|||
<ClCompile Include="..\..\src\CxbxKrnl\crc32c.cpp">
|
||||
<Filter>Shared</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\devices\video\swizzle.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\CxbxKrnl\EmuKrnlKi.cpp">
|
||||
<Filter>Kernel</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\devices\EmuNVNet.cpp">
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\devices\video\glextensions.c">
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\devices\video\gloffscreen.c">
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\devices\video\nv2a.cpp">
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\devices\video\nv2a_debug.c">
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\devices\video\nv2a_psh.cpp">
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\devices\video\nv2a_shaders.cpp">
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\devices\video\nv2a_vsh.cpp">
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\devices\video\swizzle.cpp">
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\src\Cxbx\DlgControllerConfig.h">
|
||||
|
@ -447,27 +465,15 @@
|
|||
<ClInclude Include="..\..\src\devices\SMDevice.h">
|
||||
<Filter>Hardware</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\devices\video\nv2a.h">
|
||||
<Filter>Hardware</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\devices\LED.h">
|
||||
<Filter>Hardware</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\devices\Xbox.h">
|
||||
<Filter>Hardware</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\devices\EmuNVNet.h">
|
||||
<Filter>Hardware</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\devices\video\nv2a_int.h">
|
||||
<Filter>Hardware</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\Common\CxbxDebugger.h">
|
||||
<Filter>Shared</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\devices\video\vga.h">
|
||||
<Filter>Hardware</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\Common\XbePrinter.h">
|
||||
<Filter>Shared</Filter>
|
||||
</ClInclude>
|
||||
|
@ -483,8 +489,44 @@
|
|||
<ClInclude Include="..\..\src\CxbxKrnl\EmuKrnlKi.h">
|
||||
<Filter>Kernel</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\devices\EmuNVNet.h">
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\devices\video\glextensions.h">
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\devices\video\gloffscreen.h">
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\devices\video\nv2a.h">
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\devices\video\nv2a_debug.h">
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\devices\video\nv2a_int.h">
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\devices\video\nv2a_psh.h">
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\devices\video\nv2a_shaders.h">
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\devices\video\nv2a_shaders_common.h">
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\devices\video\nv2a_vsh.h">
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\devices\video\swizzle.h">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\devices\video\vga.h">
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\devices\video\queue.h">
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -741,67 +783,67 @@
|
|||
<Filter>HLEDatabase\XOnline</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\devices\video\EmuNV2A_DEBUG.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\devices\video\EmuNV2A_PBUS.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\devices\video\EmuNV2A_PCOUNTER.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\devices\video\EmuNV2A_PCRTC.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\devices\video\EmuNV2A_PFB.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\devices\video\EmuNV2A_PFIFO.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\devices\video\EmuNV2A_PGRAPH.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\devices\video\EmuNV2A_PMC.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\devices\video\EmuNV2A_PRAMDAC.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\devices\video\EmuNV2A_PRAMIN.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\devices\video\EmuNV2A_PRMA.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\devices\video\EmuNV2A_PRMCIO.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\devices\video\EmuNV2A_PRMDIO.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\devices\video\EmuNV2A_PRMFB.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\devices\video\EmuNV2A_PRMVIO.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\devices\video\EmuNV2A_PSTRAPS.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\devices\video\EmuNV2A_PTIMER.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\devices\video\EmuNV2A_PTV.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\devices\video\EmuNV2A_PVIDEO.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\devices\video\EmuNV2A_PVPE.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</None>
|
||||
<None Include="..\..\src\devices\video\EmuNV2A_USER.cpp">
|
||||
<Filter>Hardware</Filter>
|
||||
<Filter>Hardware\Video</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -847,6 +889,9 @@
|
|||
<Filter Include="Hardware">
|
||||
<UniqueIdentifier>{922ab09b-aa8e-41bb-b781-58654160ee3d}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Hardware\Video">
|
||||
<UniqueIdentifier>{deba5d3e-9a1a-4099-bc91-12737a48272e}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="..\..\resource\Cxbx.rc">
|
||||
|
|
|
@ -1083,17 +1083,18 @@ __declspec(noreturn) void CxbxKrnlInit
|
|||
|
||||
SetupXboxDeviceTypes();
|
||||
|
||||
InitXboxHardware(HardwareModel::Revision1_5); // TODO : Make configurable
|
||||
|
||||
// Now the hardware devices exist, couple the EEPROM buffer to it's device
|
||||
g_EEPROM->SetEEPROM((uint8_t*)EEPROM);
|
||||
|
||||
if (bLLE_GPU)
|
||||
{
|
||||
DbgPrintf("INIT: Initializing OpenGL.\n");
|
||||
InitOpenGLContext();
|
||||
}
|
||||
else
|
||||
|
||||
InitXboxHardware(HardwareModel::Revision1_5); // TODO : Make configurable
|
||||
|
||||
// Now the hardware devices exist, couple the EEPROM buffer to it's device
|
||||
g_EEPROM->SetEEPROM((uint8_t*)EEPROM);
|
||||
|
||||
if (!bLLE_GPU)
|
||||
{
|
||||
DbgPrintf("INIT: Initializing Direct3D.\n");
|
||||
XTL::EmuD3DInit();
|
||||
|
|
|
@ -366,9 +366,7 @@ int pfifo_puller_thread(NV2AState *d)
|
|||
|
||||
Cache1State *state = &(d->pfifo.cache1);
|
||||
|
||||
#ifdef COMPILE_OPENGL
|
||||
glo_set_current(d->pgraph.gl_context);
|
||||
#endif
|
||||
// glo_set_current(d->pgraph.gl_context);
|
||||
|
||||
std::unique_lock<std::mutex> cache_unique_lock(d->pfifo.cache1.cache_lock, std::defer_lock);
|
||||
|
||||
|
@ -381,9 +379,9 @@ int pfifo_puller_thread(NV2AState *d)
|
|||
|
||||
if (d->exiting) {
|
||||
cache_unique_lock.unlock(); // UNTESTED
|
||||
#ifdef COMPILE_OPENGL
|
||||
glo_set_current(NULL);
|
||||
#endif
|
||||
|
||||
// glo_set_current(NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,368 @@
|
|||
/* g-lru-cache.c
|
||||
*
|
||||
* Copyright (C) 2009 - Christian Hergert
|
||||
*
|
||||
* This is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Ideally, you want to use fast_get. This is because we are using a
|
||||
* GStaticRWLock which is indeed slower than a mutex if you have lots of writer
|
||||
* acquisitions. This doesn't make it a true LRU, though, as the oldest
|
||||
* retrieval from strorage is the first item evicted.
|
||||
*/
|
||||
|
||||
#include "g-lru-cache.h"
|
||||
|
||||
#define LRU_CACHE_PRIVATE(object) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE((object), \
|
||||
G_TYPE_LRU_CACHE, \
|
||||
GLruCachePrivate))
|
||||
|
||||
struct _GLruCachePrivate
|
||||
{
|
||||
GRWLock rw_lock;
|
||||
guint max_size;
|
||||
gboolean fast_get;
|
||||
|
||||
GHashTable *hash_table;
|
||||
GEqualFunc key_equal_func;
|
||||
GCopyFunc key_copy_func;
|
||||
GList *newest;
|
||||
GList *oldest;
|
||||
|
||||
GLookupFunc retrieve_func;
|
||||
|
||||
gpointer user_data;
|
||||
GDestroyNotify user_destroy_func;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GLruCache, g_lru_cache, G_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
g_lru_cache_finalize (GObject *object)
|
||||
{
|
||||
GLruCachePrivate *priv = LRU_CACHE_PRIVATE (object);
|
||||
|
||||
if (priv->user_data && priv->user_destroy_func)
|
||||
priv->user_destroy_func (priv->user_data);
|
||||
|
||||
priv->user_data = NULL;
|
||||
priv->user_destroy_func = NULL;
|
||||
|
||||
g_hash_table_destroy (priv->hash_table);
|
||||
priv->hash_table = NULL;
|
||||
|
||||
g_list_free (priv->newest);
|
||||
priv->newest = NULL;
|
||||
priv->oldest = NULL;
|
||||
|
||||
G_OBJECT_CLASS (g_lru_cache_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
g_lru_cache_class_init (GLruCacheClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = g_lru_cache_finalize;
|
||||
|
||||
g_type_class_add_private (object_class, sizeof (GLruCachePrivate));
|
||||
}
|
||||
|
||||
static void
|
||||
g_lru_cache_init (GLruCache *self)
|
||||
{
|
||||
self->priv = LRU_CACHE_PRIVATE (self);
|
||||
|
||||
self->priv->max_size = 1024;
|
||||
self->priv->fast_get = FALSE;
|
||||
g_rw_lock_init (&self->priv->rw_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
g_lru_cache_evict_n_oldest_locked (GLruCache *self, gint n)
|
||||
{
|
||||
GList *victim;
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
victim = self->priv->oldest;
|
||||
|
||||
if (victim == NULL)
|
||||
return;
|
||||
|
||||
if (victim->prev)
|
||||
victim->prev->next = NULL;
|
||||
|
||||
self->priv->oldest = victim->prev;
|
||||
g_hash_table_remove (self->priv->hash_table, victim->data);
|
||||
|
||||
if (self->priv->newest == victim)
|
||||
self->priv->newest = NULL;
|
||||
|
||||
g_list_free1 (victim); /* victim->data is owned by hashtable */
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
g_assert (g_hash_table_size (self->priv->hash_table) == g_list_length (self->priv->newest));
|
||||
#endif
|
||||
}
|
||||
|
||||
GLruCache*
|
||||
g_lru_cache_new (GHashFunc key_hash_func,
|
||||
GEqualFunc key_equal_func,
|
||||
GLookupFunc retrieve_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy_func)
|
||||
{
|
||||
return g_lru_cache_new_full (0,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
key_hash_func,
|
||||
key_equal_func,
|
||||
retrieve_func,
|
||||
user_data,
|
||||
user_destroy_func);
|
||||
}
|
||||
|
||||
GLruCache*
|
||||
g_lru_cache_new_full (GType key_type,
|
||||
GCopyFunc key_copy_func,
|
||||
GDestroyNotify key_destroy_func,
|
||||
GType value_type,
|
||||
GCopyFunc value_copy_func,
|
||||
GDestroyNotify value_destroy_func,
|
||||
GHashFunc key_hash_func,
|
||||
GEqualFunc key_equal_func,
|
||||
GLookupFunc retrieve_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy_func)
|
||||
{
|
||||
GLruCache *self = g_object_new (G_TYPE_LRU_CACHE, NULL);
|
||||
|
||||
self->priv->hash_table = g_hash_table_new_full (key_hash_func,
|
||||
key_equal_func,
|
||||
key_destroy_func,
|
||||
value_destroy_func);
|
||||
|
||||
self->priv->key_equal_func = key_equal_func;
|
||||
self->priv->key_copy_func = key_copy_func;
|
||||
self->priv->retrieve_func = retrieve_func;
|
||||
self->priv->user_data = user_data;
|
||||
self->priv->user_destroy_func = user_destroy_func;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
g_lru_cache_set_max_size (GLruCache *self, guint max_size)
|
||||
{
|
||||
g_return_if_fail (G_IS_LRU_CACHE (self));
|
||||
|
||||
guint old_max_size = self->priv->max_size;
|
||||
|
||||
g_rw_lock_writer_lock (&(self->priv->rw_lock));
|
||||
|
||||
self->priv->max_size = max_size;
|
||||
|
||||
if (old_max_size > max_size)
|
||||
g_lru_cache_evict_n_oldest_locked (self, old_max_size - max_size);
|
||||
|
||||
g_rw_lock_writer_unlock (&(self->priv->rw_lock));
|
||||
}
|
||||
|
||||
guint
|
||||
g_lru_cache_get_max_size (GLruCache *self)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_LRU_CACHE (self), -1);
|
||||
return self->priv->max_size;
|
||||
}
|
||||
|
||||
guint
|
||||
g_lru_cache_get_size (GLruCache *self)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_LRU_CACHE (self), -1);
|
||||
return g_hash_table_size (self->priv->hash_table);
|
||||
}
|
||||
|
||||
gpointer
|
||||
g_lru_cache_get (GLruCache *self, gpointer key, GError **error)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_LRU_CACHE (self), NULL);
|
||||
|
||||
gpointer value;
|
||||
GError *retrieve_error = NULL;
|
||||
|
||||
g_rw_lock_reader_lock (&(self->priv->rw_lock));
|
||||
|
||||
value = g_hash_table_lookup (self->priv->hash_table, key);
|
||||
|
||||
#if DEBUG
|
||||
if (value)
|
||||
g_debug ("Cache Hit!");
|
||||
else
|
||||
g_debug ("Cache miss");
|
||||
#endif
|
||||
|
||||
g_rw_lock_reader_unlock (&(self->priv->rw_lock));
|
||||
|
||||
if (!value)
|
||||
{
|
||||
g_rw_lock_writer_lock (&(self->priv->rw_lock));
|
||||
|
||||
if (!g_hash_table_lookup (self->priv->hash_table, key))
|
||||
{
|
||||
if (g_hash_table_size (self->priv->hash_table) >= self->priv->max_size)
|
||||
#if DEBUG
|
||||
{
|
||||
g_debug ("We are at capacity, must evict oldest");
|
||||
#endif
|
||||
g_lru_cache_evict_n_oldest_locked (self, 1);
|
||||
#if DEBUG
|
||||
}
|
||||
|
||||
g_debug ("Retrieving value from external resource");
|
||||
#endif
|
||||
|
||||
value = self->priv->retrieve_func (key,
|
||||
self->priv->user_data,
|
||||
&retrieve_error);
|
||||
|
||||
if (G_UNLIKELY (retrieve_error != NULL))
|
||||
{
|
||||
g_propagate_error (error, retrieve_error);
|
||||
return value; /* likely 'NULL', but we should be transparent */
|
||||
}
|
||||
|
||||
if (self->priv->key_copy_func)
|
||||
g_hash_table_insert (self->priv->hash_table,
|
||||
self->priv->key_copy_func (key, self->priv->user_data),
|
||||
value);
|
||||
else
|
||||
g_hash_table_insert (self->priv->hash_table, key, value);
|
||||
|
||||
self->priv->newest = g_list_prepend (self->priv->newest, key);
|
||||
|
||||
if (self->priv->oldest == NULL)
|
||||
self->priv->oldest = self->priv->newest;
|
||||
}
|
||||
#if DEBUG
|
||||
else g_debug ("Lost storage race with another thread");
|
||||
#endif
|
||||
|
||||
g_rw_lock_writer_unlock (&(self->priv->rw_lock));
|
||||
}
|
||||
|
||||
/* fast_get means that we do not reposition the item to the head
|
||||
* of the list. it essentially makes the lru, a lru from storage,
|
||||
* not lru to user.
|
||||
*/
|
||||
|
||||
else if (!self->priv->fast_get &&
|
||||
!self->priv->key_equal_func (key, self->priv->newest->data))
|
||||
{
|
||||
#if DEBUG
|
||||
g_debug ("Making item most recent");
|
||||
#endif
|
||||
|
||||
g_rw_lock_writer_lock (&(self->priv->rw_lock));
|
||||
|
||||
GList *list = self->priv->newest;
|
||||
GList *tmp;
|
||||
GEqualFunc equal = self->priv->key_equal_func;
|
||||
|
||||
for (tmp = list; tmp; tmp = tmp->next)
|
||||
{
|
||||
if (equal (key, tmp->data))
|
||||
{
|
||||
GList *tmp1 = g_list_remove_link (list, tmp);
|
||||
self->priv->newest = g_list_prepend (tmp1, tmp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_rw_lock_writer_unlock (&(self->priv->rw_lock));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void
|
||||
g_lru_cache_evict (GLruCache *self, gpointer key)
|
||||
{
|
||||
g_return_if_fail (G_IS_LRU_CACHE (self));
|
||||
|
||||
GEqualFunc equal = self->priv->key_equal_func;
|
||||
GList *list = NULL;
|
||||
|
||||
g_rw_lock_writer_lock (&(self->priv->rw_lock));
|
||||
|
||||
if (equal (key, self->priv->oldest))
|
||||
{
|
||||
g_lru_cache_evict_n_oldest_locked (self, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_hash_table_remove (self->priv->hash_table, key);
|
||||
|
||||
for (list = self->priv->newest; list; list = list->next)
|
||||
{
|
||||
if (equal (key, list->data))
|
||||
{
|
||||
self->priv->newest = g_list_remove_link (self->priv->newest, list);
|
||||
g_list_free (list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_rw_lock_writer_unlock (&(self->priv->rw_lock));
|
||||
}
|
||||
|
||||
void
|
||||
g_lru_cache_clear (GLruCache *self)
|
||||
{
|
||||
g_return_if_fail (G_IS_LRU_CACHE (self));
|
||||
|
||||
g_rw_lock_writer_lock (&(self->priv->rw_lock));
|
||||
|
||||
g_hash_table_remove_all (self->priv->hash_table);
|
||||
g_list_free (self->priv->newest);
|
||||
|
||||
self->priv->oldest = NULL;
|
||||
self->priv->newest = NULL;
|
||||
|
||||
g_rw_lock_writer_unlock (&(self->priv->rw_lock));
|
||||
}
|
||||
|
||||
void
|
||||
g_lru_cache_set_fast_get (GLruCache *self, gboolean fast_get)
|
||||
{
|
||||
g_return_if_fail (G_IS_LRU_CACHE (self));
|
||||
self->priv->fast_get = fast_get;
|
||||
}
|
||||
|
||||
gboolean
|
||||
g_lru_cache_get_fast_get (GLruCache *self)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_LRU_CACHE (self), FALSE);
|
||||
return self->priv->fast_get;
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/* g-lru-cache.h
|
||||
*
|
||||
* Copyright (C) 2009 - Christian Hergert
|
||||
*
|
||||
* This is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __G_LRU_CACHE_H__
|
||||
#define __G_LRU_CACHE_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_LRU_CACHE (g_lru_cache_get_type ())
|
||||
#define G_LRU_CACHE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_LRU_CACHE, GLruCache))
|
||||
#define G_LRU_CACHE_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_LRU_CACHE, GLruCache const))
|
||||
#define G_LRU_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_LRU_CACHE, GLruCacheClass))
|
||||
#define G_IS_LRU_CACHE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_LRU_CACHE))
|
||||
#define G_IS_LRU_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_LRU_CACHE))
|
||||
#define G_LRU_CACHE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_LRU_CACHE, GLruCacheClass))
|
||||
#define G_LOOKUP_FUNC(func) ((GLookupFunc)func)
|
||||
|
||||
typedef struct _GLruCache GLruCache;
|
||||
typedef struct _GLruCacheClass GLruCacheClass;
|
||||
typedef struct _GLruCachePrivate GLruCachePrivate;
|
||||
|
||||
typedef gpointer (*GLookupFunc) (gpointer key, gpointer user_data, GError **error);
|
||||
|
||||
struct _GLruCache
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
GLruCachePrivate *priv;
|
||||
};
|
||||
|
||||
struct _GLruCacheClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
GType g_lru_cache_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GLruCache* g_lru_cache_new (GHashFunc key_hash_func,
|
||||
GEqualFunc key_equal_func,
|
||||
GLookupFunc retrieve_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy_func);
|
||||
|
||||
GLruCache* g_lru_cache_new_full (GType key_type,
|
||||
GCopyFunc key_copy_func,
|
||||
GDestroyNotify key_destroy_func,
|
||||
GType value_type,
|
||||
GCopyFunc value_copy_func,
|
||||
GDestroyNotify value_destroy_func,
|
||||
GHashFunc key_hash_func,
|
||||
GEqualFunc key_equal_func,
|
||||
GLookupFunc retrieve_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy_func);
|
||||
|
||||
void g_lru_cache_set_max_size (GLruCache *self, guint max_size);
|
||||
guint g_lru_cache_get_max_size (GLruCache *self);
|
||||
|
||||
guint g_lru_cache_get_size (GLruCache *self);
|
||||
|
||||
gpointer g_lru_cache_get (GLruCache *self, gpointer key, GError **error);
|
||||
void g_lru_cache_evict (GLruCache *self, gpointer key);
|
||||
void g_lru_cache_clear (GLruCache *self);
|
||||
|
||||
gboolean g_lru_cache_get_fast_get (GLruCache *self);
|
||||
void g_lru_cache_set_fast_get (GLruCache *self, gboolean fast_get);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __G_LRU_CACHE_H__ */
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* QEMU OpenGL extensions
|
||||
*
|
||||
* Copyright (c) 2015 espes
|
||||
*
|
||||
* 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 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "gloffscreen.h"
|
||||
#include "glextensions.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
void (*glFrameTerminatorGREMEDY)(void);
|
||||
|
||||
void (*glDebugMessageInsert) (GLenum source, GLenum type, GLuint id,
|
||||
GLenum severity, GLsizei length,
|
||||
const GLchar *buf);
|
||||
void (*glPushDebugGroup)(GLenum source, GLuint id, GLsizei length,
|
||||
const GLchar *message);
|
||||
void (*glPopDebugGroup)(void);
|
||||
void (*glObjectLabel)(GLenum identifier, GLuint name, GLsizei length,
|
||||
const GLchar *label);
|
||||
|
||||
#endif
|
||||
|
||||
void glextensions_init(void)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
glFrameTerminatorGREMEDY =
|
||||
glo_get_extension_proc("glFrameTerminatorGREMEDY");
|
||||
glDebugMessageInsert = glo_get_extension_proc("glDebugMessageInsert");
|
||||
glPushDebugGroup = glo_get_extension_proc("glPushDebugGroup");
|
||||
glPopDebugGroup = glo_get_extension_proc("glPopDebugGroup");
|
||||
glObjectLabel = glo_get_extension_proc("glObjectLabel");
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* QEMU OpenGL extensions
|
||||
*
|
||||
* Copyright (c) 2015 espes
|
||||
*
|
||||
* 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 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef GLEXTEENSIONS_H_
|
||||
#define GLEXTEENSIONS_H_
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "gloffscreen.h"
|
||||
extern void (*glFrameTerminatorGREMEDY)(void);
|
||||
|
||||
#define GL_DEBUG_SOURCE_APPLICATION 0x824A
|
||||
#define GL_DEBUG_TYPE_MARKER 0x8268
|
||||
#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B
|
||||
#define GL_DEBUG_OUTPUT 0x92E0
|
||||
|
||||
extern void (*glDebugMessageInsert) (GLenum source, GLenum type, GLuint id,
|
||||
GLenum severity, GLsizei length,
|
||||
const GLchar *buf);
|
||||
extern void (*glPushDebugGroup)(GLenum source, GLuint id, GLsizei length,
|
||||
const GLchar *message);
|
||||
extern void (*glPopDebugGroup)(void);
|
||||
extern void (*glObjectLabel)(GLenum identifier, GLuint name, GLsizei length,
|
||||
const GLchar *label);
|
||||
|
||||
#endif
|
||||
|
||||
void glextensions_init(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* Offscreen OpenGL abstraction layer - Common utilities
|
||||
*
|
||||
* Copyright (c) 2010 Intel
|
||||
* Written by:
|
||||
* Gordon Williams <gordon.williams@collabora.co.uk>
|
||||
* Ian Molton <ian.molton@collabora.co.uk>
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "gloffscreen.h"
|
||||
|
||||
|
||||
void glo_readpixels(GLenum gl_format, GLenum gl_type,
|
||||
unsigned int bytes_per_pixel, unsigned int stride,
|
||||
unsigned int width, unsigned int height, void *data)
|
||||
{
|
||||
/* TODO: weird strides */
|
||||
assert(stride % bytes_per_pixel == 0);
|
||||
|
||||
/* Save guest processes GL state before we ReadPixels() */
|
||||
int rl, pa;
|
||||
glGetIntegerv(GL_PACK_ROW_LENGTH, &rl);
|
||||
glGetIntegerv(GL_PACK_ALIGNMENT, &pa);
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, stride / bytes_per_pixel);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
|
||||
#ifdef GETCONTENTS_INDIVIDUAL
|
||||
GLubyte *b = (GLubyte *) data;
|
||||
int irow;
|
||||
|
||||
for (irow = height - 1; irow >= 0; irow--) {
|
||||
glReadPixels(0, irow, width, 1, gl_format, gl_type, b);
|
||||
b += stride;
|
||||
}
|
||||
#else
|
||||
/* Faster buffer flip */
|
||||
GLubyte *b = (GLubyte *) data;
|
||||
GLubyte *c = &((GLubyte *) data)[stride * (height - 1)];
|
||||
GLubyte *tmp = (GLubyte *) malloc(width * bytes_per_pixel);
|
||||
unsigned int irow;
|
||||
|
||||
glReadPixels(0, 0, width, height, gl_format, gl_type, data);
|
||||
|
||||
for (irow = 0; irow < height / 2; irow++) {
|
||||
memcpy(tmp, b, width * bytes_per_pixel);
|
||||
memcpy(b, c, width * bytes_per_pixel);
|
||||
memcpy(c, tmp, width * bytes_per_pixel);
|
||||
b += stride;
|
||||
c -= stride;
|
||||
}
|
||||
free(tmp);
|
||||
#endif
|
||||
|
||||
/* Restore GL state */
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, rl);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, pa);
|
||||
}
|
||||
|
||||
|
||||
bool glo_check_extension(const char* ext_name)
|
||||
{
|
||||
int i;
|
||||
int num_extensions = GL_NUM_EXTENSIONS;
|
||||
for (i=0; i<num_extensions; i++) {
|
||||
const char* ext = (const char*)glGetStringi(GL_EXTENSIONS, i);
|
||||
if (!ext) break;
|
||||
if (strcmp(ext, ext_name) == 0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Offscreen OpenGL abstraction layer - CGL (Apple) specific
|
||||
*
|
||||
* Copyright (c) 2013 Wayo
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
//#include <stdio.h>
|
||||
//#include <string.h>
|
||||
//#include <dlfcn.h>
|
||||
//
|
||||
//
|
||||
//#include <OpenGL/OpenGL.h>
|
||||
//#include <OpenGL/CGLTypes.h>
|
||||
//#include <OpenGL/CGLCurrent.h>
|
||||
//
|
||||
//#include "gloffscreen.h"
|
||||
//
|
||||
//struct _GloContext {
|
||||
// CGLContextObj cglContext;
|
||||
//};
|
||||
//
|
||||
///* Create an OpenGL context for a certain pixel format. formatflags are from
|
||||
// * the GLO_ constants */
|
||||
//GloContext *glo_context_create(void)
|
||||
//{
|
||||
// CGLError err;
|
||||
//
|
||||
// GloContext *context = (GloContext *)malloc(sizeof(GloContext));
|
||||
//
|
||||
// /* pixel format attributes */
|
||||
// CGLPixelFormatAttribute attributes[] = {
|
||||
// kCGLPFAAccelerated,
|
||||
// kCGLPFAOpenGLProfile,
|
||||
// (CGLPixelFormatAttribute)kCGLOGLPVersion_GL3_Core,
|
||||
// (CGLPixelFormatAttribute)0
|
||||
// };
|
||||
//
|
||||
// CGLPixelFormatObj pix;
|
||||
// GLint num;
|
||||
// err = CGLChoosePixelFormat(attributes, &pix, &num);
|
||||
// if (err) return NULL;
|
||||
//
|
||||
// err = CGLCreateContext(pix, NULL, &context->cglContext);
|
||||
// if (err) return NULL;
|
||||
//
|
||||
// CGLDestroyPixelFormat(pix);
|
||||
//
|
||||
// glo_set_current(context);
|
||||
//
|
||||
// return context;
|
||||
//}
|
||||
//
|
||||
//void* glo_get_extension_proc(const char* ext_proc)
|
||||
//{
|
||||
// return dlsym(RTLD_NEXT, ext_proc);
|
||||
//}
|
||||
//
|
||||
///* Set current context */
|
||||
//void glo_set_current(GloContext *context)
|
||||
//{
|
||||
// if (context == NULL) {
|
||||
// CGLSetCurrentContext(NULL);
|
||||
// } else {
|
||||
// CGLSetCurrentContext(context->cglContext);
|
||||
// }
|
||||
//}
|
||||
//
|
||||
///* Destroy a previously created OpenGL context */
|
||||
//void glo_context_destroy(GloContext *context)
|
||||
//{
|
||||
// if (!context) return;
|
||||
// glo_set_current(NULL);
|
||||
// CGLDestroyContext(context->cglContext);
|
||||
//}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Offscreen OpenGL abstraction layer
|
||||
*
|
||||
* Copyright (c) 2010 Intel
|
||||
* Written by:
|
||||
* Gordon Williams <gordon.williams@collabora.co.uk>
|
||||
* Ian Molton <ian.molton@collabora.co.uk>
|
||||
* Copyright (c) 2013 Wayo
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef GLOFFSCREEN_H_
|
||||
#define GLOFFSCREEN_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
// #ifdef __APPLE__
|
||||
// #include <OpenGL/gl3.h>
|
||||
// #include <OpenGL/glext.h>
|
||||
// #else
|
||||
// #include <GL/glew.h>
|
||||
// #include <GL/gl.h>
|
||||
// #endif
|
||||
|
||||
//#include <SDL.h>
|
||||
#include <GL/glew.h>
|
||||
|
||||
/* Used to hold data for the OpenGL context */
|
||||
struct _GloContext;
|
||||
typedef struct _GloContext GloContext;
|
||||
|
||||
/* Change current context */
|
||||
void glo_set_current(GloContext *context);
|
||||
|
||||
/* Check GL Extensions */
|
||||
bool glo_check_extension(const char* ext_name);
|
||||
void* glo_get_extension_proc(const char* extProc);
|
||||
|
||||
/* Create an OpenGL context */
|
||||
GloContext *glo_context_create(void);
|
||||
|
||||
/* Destroy a previouslu created OpenGL context */
|
||||
void glo_context_destroy(GloContext *context);
|
||||
|
||||
/* Note that this is top-down, not bottom-up as glReadPixels would do. */
|
||||
void glo_readpixels(GLenum gl_format, GLenum gl_type,
|
||||
unsigned int bytes_per_pixel, unsigned int stride,
|
||||
unsigned int width, unsigned int height, void *data);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GLOFFSCREEN_H_ */
|
|
@ -67,13 +67,34 @@ namespace xboxkrnl
|
|||
#include "vga.h"
|
||||
#include "nv2a.h" // For NV2AState
|
||||
#include "nv2a_int.h" // from https://github.com/espes/xqemu/tree/xbox/hw/xbox
|
||||
|
||||
//#include <gl\glew.h>
|
||||
#include <gl\GL.h>
|
||||
#include <gl\GLU.h>
|
||||
#include <cassert>
|
||||
//#include <gl\glut.h>
|
||||
|
||||
// glib types
|
||||
typedef char gchar;
|
||||
typedef int gint;
|
||||
typedef unsigned int guint;
|
||||
typedef unsigned int guint32;
|
||||
typedef const void *gconstpointer;
|
||||
typedef gint gboolean;
|
||||
typedef void* gpointer;
|
||||
|
||||
typedef guint32 GQuark;
|
||||
|
||||
typedef struct _GError GError;
|
||||
|
||||
struct _GError
|
||||
{
|
||||
GQuark domain;
|
||||
gint code;
|
||||
gchar *message;
|
||||
};
|
||||
|
||||
#include "glextensions.h" // for glextensions_init
|
||||
|
||||
|
||||
static void update_irq(NV2AState *d)
|
||||
{
|
||||
|
@ -151,7 +172,12 @@ static inline uint32_t ldl_le_p(const void *p)
|
|||
return *(uint32_t*)p;
|
||||
}
|
||||
|
||||
static inline void stl_le_p(uint32_t *p, uint32 v)
|
||||
static inline void stq_le_p(uint64_t *p, uint64_t v)
|
||||
{
|
||||
*p = v;
|
||||
}
|
||||
|
||||
static inline void stl_le_p(uint32_t *p, uint32_t v)
|
||||
{
|
||||
*p = v;
|
||||
}
|
||||
|
@ -618,6 +644,7 @@ void CxbxReserveNV2AMemory(NV2AState *d)
|
|||
NV2ADevice::NV2ADevice()
|
||||
{
|
||||
m_nv2a_state = new NV2AState();
|
||||
m_nv2a_state->pgraph.opengl_enabled = bLLE_GPU;
|
||||
pgraph_init(m_nv2a_state);
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,9 @@
|
|||
|
||||
#include "swizzle.h"
|
||||
#include "nv2a_int.h"
|
||||
#include "nv2a_debug.h" // For HWADDR_PRIx, NV2A_DPRINTF, NV2A_GL_DPRINTF, etc.
|
||||
#include "gloffscreen.h" // For glo_readpixels
|
||||
#include "nv2a_shaders.h" // For ShaderBinding
|
||||
|
||||
#define NV2A_ADDR 0xFD000000
|
||||
#define NV2A_SIZE 0x01000000
|
||||
|
@ -61,19 +64,33 @@
|
|||
|
||||
typedef xbaddr hwaddr; // Compatibility; Cxbx uses xbaddr, xqemu and OpenXbox use hwaddr
|
||||
typedef uint32_t value_t; // Compatibility; Cxbx values are uint32_t (xqemu and OpenXbox use uint64_t)
|
||||
#define NV2A_DPRINTF(...) printf("[0x????] NV2A: " ## __VA_ARGS__) // Compatibility; TODO : Replace this by something equivalent
|
||||
#define NV2A_GL_DPRINTF EmuWarning // Compatibility; TODO : Replace this by something equivalent
|
||||
|
||||
#ifdef __cplusplus
|
||||
template <size_t N> struct ArraySizeHelper { char _[N]; };
|
||||
template <typename T, size_t N>
|
||||
ArraySizeHelper<N> makeArraySizeHelper(T(&)[N]);
|
||||
# define ARRAY_SIZE(a) sizeof(makeArraySizeHelper(a))
|
||||
#else
|
||||
// The expression ARRAY_SIZE(a) is a compile-time constant of type
|
||||
// size_t which represents the number of elements of the given
|
||||
// array. You should only use ARRAY_SIZE on statically allocated
|
||||
// arrays.
|
||||
|
||||
#define ARRAY_SIZE(a) \
|
||||
((sizeof(a) / sizeof(*(a))) / \
|
||||
static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
|
||||
#endif
|
||||
|
||||
#define VSH_TOKEN_SIZE 4 // Compatibility; TODO : Move this to nv2a_vsh.h
|
||||
#define MAX(a,b) ((a)>(b) ? (a) : (b)) // Compatibility
|
||||
#define MIN(a,b) ((a)<(b) ? (a) : (b)) // Compatibility
|
||||
#undef COMPILE_OPENGL // Compatibility; define this to include all OpenGL calls
|
||||
#define HWADDR_PRIx "p" // Compatibility
|
||||
|
||||
#define g_free(x) free(x) // Compatibility
|
||||
#define g_malloc(x) malloc(x) // Compatibility
|
||||
#define g_malloc0(x) calloc(1, x) // Compatibility
|
||||
#define g_realloc(x, y) realloc(x, y) // Compatibility
|
||||
|
||||
#define USE_TEXTURE_CACHE
|
||||
#undef USE_TEXTURE_CACHE
|
||||
|
||||
// Public Domain ffs Implementation
|
||||
// See: http://snipplr.com/view/22147/stringsh-implementation/
|
||||
|
@ -301,6 +318,7 @@ typedef struct GraphicsContext {
|
|||
|
||||
|
||||
typedef struct PGRAPHState {
|
||||
bool opengl_enabled; // == bLLE_GPU
|
||||
std::mutex pgraph_lock;
|
||||
|
||||
uint32_t pending_interrupts;
|
||||
|
@ -333,12 +351,14 @@ typedef struct PGRAPHState {
|
|||
SurfaceShape last_surface_shape;
|
||||
|
||||
xbaddr dma_a, dma_b;
|
||||
//GLruCache *texture_cache;
|
||||
#ifdef USE_TEXTURE_CACHE
|
||||
GLruCache *texture_cache;
|
||||
#endif
|
||||
bool texture_dirty[NV2A_MAX_TEXTURES];
|
||||
TextureBinding *texture_binding[NV2A_MAX_TEXTURES];
|
||||
|
||||
//GHashTable *shader_cache;
|
||||
//ShaderBinding *shader_binding;
|
||||
ShaderBinding *shader_binding;
|
||||
|
||||
bool texture_matrix_enable[NV2A_MAX_TEXTURES];
|
||||
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* QEMU Geforce NV2A debug helpers
|
||||
*
|
||||
* Copyright (c) 2015 Jannik Vogel
|
||||
* Copyright (c) 2012 espes
|
||||
*
|
||||
* 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 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "nv2a_debug.h"
|
||||
|
||||
#ifdef DEBUG_NV2A_GL
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "gl/glextensions.h"
|
||||
|
||||
void gl_debug_message(bool cc, const char *fmt, ...)
|
||||
{
|
||||
size_t n;
|
||||
char buffer[1024];
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
n = vsnprintf(buffer, sizeof(buffer), fmt, ap);
|
||||
assert(n <= sizeof(buffer));
|
||||
va_end(ap);
|
||||
|
||||
if(glDebugMessageInsert) {
|
||||
glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_MARKER,
|
||||
0, GL_DEBUG_SEVERITY_NOTIFICATION, n, buffer);
|
||||
}
|
||||
if (cc) {
|
||||
fwrite(buffer, sizeof(char), n, stdout);
|
||||
fputc('\n', stdout);
|
||||
}
|
||||
}
|
||||
|
||||
void gl_debug_group_begin(const char *fmt, ...)
|
||||
{
|
||||
size_t n;
|
||||
char buffer[1024];
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
n = vsnprintf(buffer, sizeof(buffer), fmt, ap);
|
||||
assert(n <= sizeof(buffer));
|
||||
va_end(ap);
|
||||
|
||||
/* Check for errors before entering group */
|
||||
assert(glGetError() == GL_NO_ERROR);
|
||||
|
||||
if (glPushDebugGroup) {
|
||||
glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, n, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void gl_debug_group_end(void)
|
||||
{
|
||||
/* Check for errors when leaving group */
|
||||
assert(glGetError() == GL_NO_ERROR);
|
||||
|
||||
if (glPopDebugGroup) {
|
||||
glPopDebugGroup();
|
||||
}
|
||||
}
|
||||
|
||||
void gl_debug_label(GLenum target, GLuint name, const char *fmt, ...)
|
||||
{
|
||||
size_t n;
|
||||
char buffer[1024];
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
n = vsnprintf(buffer, sizeof(buffer), fmt, ap);
|
||||
assert(n <= sizeof(buffer));
|
||||
va_end(ap);
|
||||
|
||||
if (glObjectLabel) {
|
||||
glObjectLabel(target, name, n, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* QEMU Geforce NV2A debug helpers
|
||||
*
|
||||
* Copyright (c) 2015 Jannik Vogel
|
||||
* Copyright (c) 2012 espes
|
||||
*
|
||||
* 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 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef HW_NV2A_DEBUG_H
|
||||
#define HW_NV2A_DEBUG_H
|
||||
|
||||
#define HWADDR_PRIx "x"
|
||||
|
||||
#define DEBUG_NV2A
|
||||
#ifdef DEBUG_NV2A
|
||||
# define NV2A_DPRINTF(format, ...) printf("[0x????] NV2A: " format, ## __VA_ARGS__)
|
||||
#else
|
||||
# define NV2A_DPRINTF(format, ...) do { } while (0)
|
||||
#endif
|
||||
|
||||
// #define DEBUG_NV2A_GL
|
||||
#ifdef DEBUG_NV2A_GL
|
||||
|
||||
#include <stdbool.h>
|
||||
// #include "gl/gloffscreen.h"
|
||||
|
||||
void gl_debug_message(bool cc, const char *fmt, ...);
|
||||
void gl_debug_group_begin(const char *fmt, ...);
|
||||
void gl_debug_group_end(void);
|
||||
void gl_debug_label(GLenum target, GLuint name, const char *fmt, ...);
|
||||
|
||||
# define NV2A_GL_DPRINTF(cc, format, ...) \
|
||||
gl_debug_message(cc, "nv2a: " format, ## __VA_ARGS__)
|
||||
# define NV2A_GL_DGROUP_BEGIN(format, ...) \
|
||||
gl_debug_group_begin("nv2a: " format, ## __VA_ARGS__)
|
||||
# define NV2A_GL_DGROUP_END() \
|
||||
gl_debug_group_end()
|
||||
# define NV2A_GL_DLABEL(target, name, format, ...) \
|
||||
gl_debug_label(target, name, "nv2a: { " format " }", ## __VA_ARGS__)
|
||||
|
||||
#else
|
||||
# define NV2A_GL_DPRINTF(cc, format, ...) do { \
|
||||
if (cc) NV2A_DPRINTF(format "\n", ##__VA_ARGS__ ); \
|
||||
} while (0)
|
||||
# define NV2A_GL_DGROUP_BEGIN(format, ...) do { } while (0)
|
||||
# define NV2A_GL_DGROUP_END() do { } while (0)
|
||||
# define NV2A_GL_DLABEL(target, name, format, ...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,865 @@
|
|||
/*
|
||||
* QEMU Geforce NV2A pixel shader translation
|
||||
*
|
||||
* Copyright (c) 2013 espes
|
||||
* Copyright (c) 2015 Jannik Vogel
|
||||
*
|
||||
* Based on:
|
||||
* Cxbx, PixelShader.cpp
|
||||
* Copyright (c) 2004 Aaron Robinson <caustik@caustik.com>
|
||||
* Kingofc <kingofc@freenet.de>
|
||||
* Xeon, XBD3DPixelShader.cpp
|
||||
* Copyright (c) 2003 _SF_
|
||||
*
|
||||
* 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 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "nv2a_shaders_common.h"
|
||||
#include "nv2a_psh.h"
|
||||
|
||||
// fixme: clean this up (i'm a lazy bastard)
|
||||
#define qstring_append_fmt(str, ...) do { \
|
||||
char buf[128]; \
|
||||
snprintf(buf, sizeof(buf), __VA_ARGS__); \
|
||||
str->append(buf); \
|
||||
} while (0) \
|
||||
|
||||
#define qstring_get_str(str) str->c_str()
|
||||
|
||||
static std::string *qstring_from_fmt(const char *fmt, ...)
|
||||
{
|
||||
char buf[128];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buf, sizeof(buf), fmt, args);
|
||||
va_end(args);
|
||||
return new std::string(buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* This implements translation of register combiners into glsl
|
||||
* fragment shaders, but all terminology is in terms of Xbox DirectX
|
||||
* pixel shaders, since I wanted to be lazy while referencing existing
|
||||
* work / stealing code.
|
||||
*
|
||||
* For some background, see the OpenGL extension:
|
||||
* https://www.opengl.org/registry/specs/NV/register_combiners.txt
|
||||
*/
|
||||
|
||||
enum PS_TEXTUREMODES
|
||||
{ // valid in stage 0 1 2 3
|
||||
PS_TEXTUREMODES_NONE= 0x00L, // * * * *
|
||||
PS_TEXTUREMODES_PROJECT2D= 0x01L, // * * * *
|
||||
PS_TEXTUREMODES_PROJECT3D= 0x02L, // * * * *
|
||||
PS_TEXTUREMODES_CUBEMAP= 0x03L, // * * * *
|
||||
PS_TEXTUREMODES_PASSTHRU= 0x04L, // * * * *
|
||||
PS_TEXTUREMODES_CLIPPLANE= 0x05L, // * * * *
|
||||
PS_TEXTUREMODES_BUMPENVMAP= 0x06L, // - * * *
|
||||
PS_TEXTUREMODES_BUMPENVMAP_LUM= 0x07L, // - * * *
|
||||
PS_TEXTUREMODES_BRDF= 0x08L, // - - * *
|
||||
PS_TEXTUREMODES_DOT_ST= 0x09L, // - - * *
|
||||
PS_TEXTUREMODES_DOT_ZW= 0x0aL, // - - * *
|
||||
PS_TEXTUREMODES_DOT_RFLCT_DIFF= 0x0bL, // - - * -
|
||||
PS_TEXTUREMODES_DOT_RFLCT_SPEC= 0x0cL, // - - - *
|
||||
PS_TEXTUREMODES_DOT_STR_3D= 0x0dL, // - - - *
|
||||
PS_TEXTUREMODES_DOT_STR_CUBE= 0x0eL, // - - - *
|
||||
PS_TEXTUREMODES_DPNDNT_AR= 0x0fL, // - * * *
|
||||
PS_TEXTUREMODES_DPNDNT_GB= 0x10L, // - * * *
|
||||
PS_TEXTUREMODES_DOTPRODUCT= 0x11L, // - * * -
|
||||
PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST= 0x12L, // - - - *
|
||||
// 0x13-0x1f reserved
|
||||
};
|
||||
|
||||
enum PS_INPUTMAPPING
|
||||
{
|
||||
PS_INPUTMAPPING_UNSIGNED_IDENTITY= 0x00L, // max(0,x) OK for final combiner
|
||||
PS_INPUTMAPPING_UNSIGNED_INVERT= 0x20L, // 1 - max(0,x) OK for final combiner
|
||||
PS_INPUTMAPPING_EXPAND_NORMAL= 0x40L, // 2*max(0,x) - 1 invalid for final combiner
|
||||
PS_INPUTMAPPING_EXPAND_NEGATE= 0x60L, // 1 - 2*max(0,x) invalid for final combiner
|
||||
PS_INPUTMAPPING_HALFBIAS_NORMAL= 0x80L, // max(0,x) - 1/2 invalid for final combiner
|
||||
PS_INPUTMAPPING_HALFBIAS_NEGATE= 0xa0L, // 1/2 - max(0,x) invalid for final combiner
|
||||
PS_INPUTMAPPING_SIGNED_IDENTITY= 0xc0L, // x invalid for final combiner
|
||||
PS_INPUTMAPPING_SIGNED_NEGATE= 0xe0L, // -x invalid for final combiner
|
||||
};
|
||||
|
||||
enum PS_REGISTER
|
||||
{
|
||||
PS_REGISTER_ZERO= 0x00L, // r
|
||||
PS_REGISTER_DISCARD= 0x00L, // w
|
||||
PS_REGISTER_C0= 0x01L, // r
|
||||
PS_REGISTER_C1= 0x02L, // r
|
||||
PS_REGISTER_FOG= 0x03L, // r
|
||||
PS_REGISTER_V0= 0x04L, // r/w
|
||||
PS_REGISTER_V1= 0x05L, // r/w
|
||||
PS_REGISTER_T0= 0x08L, // r/w
|
||||
PS_REGISTER_T1= 0x09L, // r/w
|
||||
PS_REGISTER_T2= 0x0aL, // r/w
|
||||
PS_REGISTER_T3= 0x0bL, // r/w
|
||||
PS_REGISTER_R0= 0x0cL, // r/w
|
||||
PS_REGISTER_R1= 0x0dL, // r/w
|
||||
PS_REGISTER_V1R0_SUM= 0x0eL, // r
|
||||
PS_REGISTER_EF_PROD= 0x0fL, // r
|
||||
|
||||
PS_REGISTER_ONE= PS_REGISTER_ZERO | PS_INPUTMAPPING_UNSIGNED_INVERT, // OK for final combiner
|
||||
PS_REGISTER_NEGATIVE_ONE= PS_REGISTER_ZERO | PS_INPUTMAPPING_EXPAND_NORMAL, // invalid for final combiner
|
||||
PS_REGISTER_ONE_HALF= PS_REGISTER_ZERO | PS_INPUTMAPPING_HALFBIAS_NEGATE, // invalid for final combiner
|
||||
PS_REGISTER_NEGATIVE_ONE_HALF= PS_REGISTER_ZERO | PS_INPUTMAPPING_HALFBIAS_NORMAL, // invalid for final combiner
|
||||
};
|
||||
|
||||
enum PS_COMBINERCOUNTFLAGS
|
||||
{
|
||||
PS_COMBINERCOUNT_MUX_LSB= 0x0000L, // mux on r0.a lsb
|
||||
PS_COMBINERCOUNT_MUX_MSB= 0x0001L, // mux on r0.a msb
|
||||
|
||||
PS_COMBINERCOUNT_SAME_C0= 0x0000L, // c0 same in each stage
|
||||
PS_COMBINERCOUNT_UNIQUE_C0= 0x0010L, // c0 unique in each stage
|
||||
|
||||
PS_COMBINERCOUNT_SAME_C1= 0x0000L, // c1 same in each stage
|
||||
PS_COMBINERCOUNT_UNIQUE_C1= 0x0100L // c1 unique in each stage
|
||||
};
|
||||
|
||||
enum PS_COMBINEROUTPUT
|
||||
{
|
||||
PS_COMBINEROUTPUT_IDENTITY= 0x00L, // y = x
|
||||
PS_COMBINEROUTPUT_BIAS= 0x08L, // y = x - 0.5
|
||||
PS_COMBINEROUTPUT_SHIFTLEFT_1= 0x10L, // y = x*2
|
||||
PS_COMBINEROUTPUT_SHIFTLEFT_1_BIAS= 0x18L, // y = (x - 0.5)*2
|
||||
PS_COMBINEROUTPUT_SHIFTLEFT_2= 0x20L, // y = x*4
|
||||
PS_COMBINEROUTPUT_SHIFTRIGHT_1= 0x30L, // y = x/2
|
||||
|
||||
PS_COMBINEROUTPUT_AB_BLUE_TO_ALPHA= 0x80L, // RGB only
|
||||
|
||||
PS_COMBINEROUTPUT_CD_BLUE_TO_ALPHA= 0x40L, // RGB only
|
||||
|
||||
PS_COMBINEROUTPUT_AB_MULTIPLY= 0x00L,
|
||||
PS_COMBINEROUTPUT_AB_DOT_PRODUCT= 0x02L, // RGB only
|
||||
|
||||
PS_COMBINEROUTPUT_CD_MULTIPLY= 0x00L,
|
||||
PS_COMBINEROUTPUT_CD_DOT_PRODUCT= 0x01L, // RGB only
|
||||
|
||||
PS_COMBINEROUTPUT_AB_CD_SUM= 0x00L, // 3rd output is AB+CD
|
||||
PS_COMBINEROUTPUT_AB_CD_MUX= 0x04L, // 3rd output is MUX(AB,CD) based on R0.a
|
||||
};
|
||||
|
||||
enum PS_CHANNEL
|
||||
{
|
||||
PS_CHANNEL_RGB= 0x00, // used as RGB source
|
||||
PS_CHANNEL_BLUE= 0x00, // used as ALPHA source
|
||||
PS_CHANNEL_ALPHA= 0x10, // used as RGB or ALPHA source
|
||||
};
|
||||
|
||||
|
||||
enum PS_FINALCOMBINERSETTING
|
||||
{
|
||||
PS_FINALCOMBINERSETTING_CLAMP_SUM= 0x80, // V1+R0 sum clamped to [0,1]
|
||||
|
||||
PS_FINALCOMBINERSETTING_COMPLEMENT_V1= 0x40, // unsigned invert mapping
|
||||
|
||||
PS_FINALCOMBINERSETTING_COMPLEMENT_R0= 0x20, // unsigned invert mapping
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Structures to describe the PS definition
|
||||
|
||||
struct InputInfo {
|
||||
int reg, mod, chan;
|
||||
bool invert;
|
||||
};
|
||||
|
||||
struct InputVarInfo {
|
||||
struct InputInfo a, b, c, d;
|
||||
};
|
||||
|
||||
struct FCInputInfo {
|
||||
struct InputInfo a, b, c, d, e, f, g;
|
||||
int c0, c1;
|
||||
//uint32_t c0_value, c1_value;
|
||||
bool c0_used, c1_used;
|
||||
bool v1r0_sum, clamp_sum, inv_v1, inv_r0, enabled;
|
||||
};
|
||||
|
||||
struct OutputInfo {
|
||||
int ab, cd, muxsum, flags, ab_op, cd_op, muxsum_op,
|
||||
mapping, ab_alphablue, cd_alphablue;
|
||||
};
|
||||
|
||||
struct PSStageInfo {
|
||||
struct InputVarInfo rgb_input, alpha_input;
|
||||
struct OutputInfo rgb_output, alpha_output;
|
||||
int c0, c1;
|
||||
//uint32_t c0_value, c1_value;
|
||||
bool c0_used, c1_used;
|
||||
};
|
||||
|
||||
struct PixelShader {
|
||||
PshState state;
|
||||
|
||||
int num_stages, flags;
|
||||
struct PSStageInfo stage[8];
|
||||
struct FCInputInfo final_input;
|
||||
int tex_modes[4], input_tex[4];
|
||||
|
||||
//uint32_t dot_mapping, input_texture;
|
||||
|
||||
std::string *varE, *varF;
|
||||
std::string *code;
|
||||
int cur_stage;
|
||||
|
||||
int num_var_refs;
|
||||
char var_refs[32][32];
|
||||
int num_const_refs;
|
||||
char const_refs[32][32];
|
||||
};
|
||||
|
||||
static void add_var_ref(struct PixelShader *ps, const char *var)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<ps->num_var_refs; i++) {
|
||||
if (strcmp((char*)ps->var_refs[i], var) == 0) return;
|
||||
}
|
||||
strcpy((char*)ps->var_refs[ps->num_var_refs++], var);
|
||||
}
|
||||
|
||||
static void add_const_ref(struct PixelShader *ps, const char *var)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<ps->num_const_refs; i++) {
|
||||
if (strcmp((char*)ps->const_refs[i], var) == 0) return;
|
||||
}
|
||||
strcpy((char*)ps->const_refs[ps->num_const_refs++], var);
|
||||
}
|
||||
|
||||
// Get the code for a variable used in the program
|
||||
static std::string* get_var(struct PixelShader *ps, int reg, bool is_dest)
|
||||
{
|
||||
switch (reg) {
|
||||
case PS_REGISTER_DISCARD:
|
||||
if (is_dest) {
|
||||
return new std::string("");
|
||||
} else {
|
||||
return new std::string("0.0");
|
||||
}
|
||||
break;
|
||||
case PS_REGISTER_C0:
|
||||
/* TODO: should the final stage really always be unique? */
|
||||
if (ps->flags & PS_COMBINERCOUNT_UNIQUE_C0 || ps->cur_stage == 8) {
|
||||
std::string *reg = qstring_from_fmt("c_%d_%d", ps->cur_stage, 0);
|
||||
add_const_ref(ps, qstring_get_str(reg));
|
||||
if (ps->cur_stage == 8) {
|
||||
ps->final_input.c0_used = true;
|
||||
} else {
|
||||
ps->stage[ps->cur_stage].c0_used = true;
|
||||
}
|
||||
return reg;
|
||||
} else { // Same c0
|
||||
add_const_ref(ps, "c_0_0");
|
||||
ps->stage[0].c0_used = true;
|
||||
return new std::string("c_0_0");
|
||||
}
|
||||
break;
|
||||
case PS_REGISTER_C1:
|
||||
if (ps->flags & PS_COMBINERCOUNT_UNIQUE_C1 || ps->cur_stage == 8) {
|
||||
std::string *reg = qstring_from_fmt("c_%d_%d", ps->cur_stage, 1);
|
||||
add_const_ref(ps, qstring_get_str(reg));
|
||||
if (ps->cur_stage == 8) {
|
||||
ps->final_input.c1_used = true;
|
||||
} else {
|
||||
ps->stage[ps->cur_stage].c1_used = true;
|
||||
}
|
||||
return reg;
|
||||
} else { // Same c1
|
||||
add_const_ref(ps, "c_0_1");
|
||||
ps->stage[0].c1_used = true;
|
||||
return new std::string("c_0_1");
|
||||
}
|
||||
break;
|
||||
case PS_REGISTER_FOG:
|
||||
return new std::string("pFog");
|
||||
case PS_REGISTER_V0:
|
||||
return new std::string("v0");
|
||||
case PS_REGISTER_V1:
|
||||
return new std::string("v1");
|
||||
case PS_REGISTER_T0:
|
||||
return new std::string("t0");
|
||||
case PS_REGISTER_T1:
|
||||
return new std::string("t1");
|
||||
case PS_REGISTER_T2:
|
||||
return new std::string("t2");
|
||||
case PS_REGISTER_T3:
|
||||
return new std::string("t3");
|
||||
case PS_REGISTER_R0:
|
||||
add_var_ref(ps, "r0");
|
||||
return new std::string("r0");
|
||||
case PS_REGISTER_R1:
|
||||
add_var_ref(ps, "r1");
|
||||
return new std::string("r1");
|
||||
case PS_REGISTER_V1R0_SUM:
|
||||
add_var_ref(ps, "r0");
|
||||
return new std::string("(v1 + r0)");
|
||||
case PS_REGISTER_EF_PROD:
|
||||
return qstring_from_fmt("(%s * %s)", qstring_get_str(ps->varE),
|
||||
qstring_get_str(ps->varF));
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Get input variable code
|
||||
static std::string* get_input_var(struct PixelShader *ps, struct InputInfo in, bool is_alpha)
|
||||
{
|
||||
std::string *reg = get_var(ps, in.reg, false);
|
||||
|
||||
if (strcmp(qstring_get_str(reg), "0.0") != 0
|
||||
&& (in.reg != PS_REGISTER_EF_PROD
|
||||
|| strstr(qstring_get_str(reg), ".a") == NULL)) {
|
||||
switch (in.chan) {
|
||||
case PS_CHANNEL_RGB:
|
||||
if (is_alpha) {
|
||||
reg->append(".b");
|
||||
} else {
|
||||
reg->append(".rgb");
|
||||
}
|
||||
break;
|
||||
case PS_CHANNEL_ALPHA:
|
||||
reg->append(".a");
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string *res;
|
||||
switch (in.mod) {
|
||||
case PS_INPUTMAPPING_SIGNED_IDENTITY:
|
||||
case PS_INPUTMAPPING_UNSIGNED_IDENTITY:
|
||||
return reg;
|
||||
case PS_INPUTMAPPING_UNSIGNED_INVERT:
|
||||
res = qstring_from_fmt("(1.0 - %s)", qstring_get_str(reg));
|
||||
break;
|
||||
case PS_INPUTMAPPING_EXPAND_NORMAL: // TODO: Change to max(0, x)??
|
||||
res = qstring_from_fmt("(2.0 * %s - 1.0)", qstring_get_str(reg));
|
||||
break;
|
||||
case PS_INPUTMAPPING_EXPAND_NEGATE:
|
||||
res = qstring_from_fmt("(1.0 - 2.0 * %s)", qstring_get_str(reg));
|
||||
break;
|
||||
case PS_INPUTMAPPING_HALFBIAS_NORMAL:
|
||||
res = qstring_from_fmt("(%s - 0.5)", qstring_get_str(reg));
|
||||
break;
|
||||
case PS_INPUTMAPPING_HALFBIAS_NEGATE:
|
||||
res = qstring_from_fmt("(0.5 - %s)", qstring_get_str(reg));
|
||||
break;
|
||||
case PS_INPUTMAPPING_SIGNED_NEGATE:
|
||||
res = qstring_from_fmt("-%s", qstring_get_str(reg));
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
delete reg;
|
||||
return res;
|
||||
}
|
||||
|
||||
// Get code for the output mapping of a stage
|
||||
static std::string* get_output(std::string *reg, int mapping)
|
||||
{
|
||||
std::string *res;
|
||||
|
||||
switch (mapping) {
|
||||
case PS_COMBINEROUTPUT_IDENTITY:
|
||||
res = reg;
|
||||
case PS_COMBINEROUTPUT_BIAS:
|
||||
res = qstring_from_fmt("(%s - 0.5)", qstring_get_str(reg));
|
||||
break;
|
||||
case PS_COMBINEROUTPUT_SHIFTLEFT_1:
|
||||
res = qstring_from_fmt("(%s * 2.0)", qstring_get_str(reg));
|
||||
break;
|
||||
case PS_COMBINEROUTPUT_SHIFTLEFT_1_BIAS:
|
||||
res = qstring_from_fmt("((%s - 0.5) * 2.0)", qstring_get_str(reg));
|
||||
break;
|
||||
case PS_COMBINEROUTPUT_SHIFTLEFT_2:
|
||||
res = qstring_from_fmt("(%s * 4.0)", qstring_get_str(reg));
|
||||
break;
|
||||
case PS_COMBINEROUTPUT_SHIFTRIGHT_1:
|
||||
res = qstring_from_fmt("(%s / 2.0)", qstring_get_str(reg));
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Add the HLSL code for a stage
|
||||
static void add_stage_code(struct PixelShader *ps,
|
||||
struct InputVarInfo input, struct OutputInfo output,
|
||||
const char *write_mask, bool is_alpha)
|
||||
{
|
||||
std::string *a = get_input_var(ps, input.a, is_alpha);
|
||||
std::string *b = get_input_var(ps, input.b, is_alpha);
|
||||
std::string *c = get_input_var(ps, input.c, is_alpha);
|
||||
std::string *d = get_input_var(ps, input.d, is_alpha);
|
||||
|
||||
const char *caster = "";
|
||||
if (strlen(write_mask) == 3) {
|
||||
caster = "vec3";
|
||||
}
|
||||
|
||||
std::string *ab;
|
||||
if (output.ab_op == PS_COMBINEROUTPUT_AB_DOT_PRODUCT) {
|
||||
ab = qstring_from_fmt("dot(%s, %s)",
|
||||
qstring_get_str(a), qstring_get_str(b));
|
||||
} else {
|
||||
ab = qstring_from_fmt("(%s * %s)",
|
||||
qstring_get_str(a), qstring_get_str(b));
|
||||
}
|
||||
|
||||
std::string *cd;
|
||||
if (output.cd_op == PS_COMBINEROUTPUT_CD_DOT_PRODUCT) {
|
||||
cd = qstring_from_fmt("dot(%s, %s)",
|
||||
qstring_get_str(c), qstring_get_str(d));
|
||||
} else {
|
||||
cd = qstring_from_fmt("(%s * %s)",
|
||||
qstring_get_str(c), qstring_get_str(d));
|
||||
}
|
||||
|
||||
std::string *ab_mapping = get_output(ab, output.mapping);
|
||||
std::string *cd_mapping = get_output(cd, output.mapping);
|
||||
std::string *ab_dest = get_var(ps, output.ab, true);
|
||||
std::string *cd_dest = get_var(ps, output.cd, true);
|
||||
std::string *sum_dest = get_var(ps, output.muxsum, true);
|
||||
|
||||
if (ab_dest->length()) {
|
||||
qstring_append_fmt(ps->code, "%s.%s = %s(%s);\n",
|
||||
qstring_get_str(ab_dest), write_mask, caster, qstring_get_str(ab_mapping));
|
||||
} else {
|
||||
// QDECREF(ab_dest);
|
||||
// QINCREF(ab_mapping);
|
||||
delete ab_dest;
|
||||
ab_dest = new std::string(*ab_mapping);
|
||||
}
|
||||
|
||||
if (cd_dest->length()) {
|
||||
qstring_append_fmt(ps->code, "%s.%s = %s(%s);\n",
|
||||
qstring_get_str(cd_dest), write_mask, caster, qstring_get_str(cd_mapping));
|
||||
} else {
|
||||
// QDECREF(cd_dest);
|
||||
// QINCREF(cd_mapping);
|
||||
delete cd_dest;
|
||||
cd_dest = new std::string(*cd_mapping);
|
||||
}
|
||||
|
||||
if (!is_alpha && output.flags & PS_COMBINEROUTPUT_AB_BLUE_TO_ALPHA) {
|
||||
qstring_append_fmt(ps->code, "%s.a = %s.b;\n",
|
||||
qstring_get_str(ab_dest), qstring_get_str(ab_dest));
|
||||
}
|
||||
if (!is_alpha && output.flags & PS_COMBINEROUTPUT_CD_BLUE_TO_ALPHA) {
|
||||
qstring_append_fmt(ps->code, "%s.a = %s.b;\n",
|
||||
qstring_get_str(cd_dest), qstring_get_str(cd_dest));
|
||||
}
|
||||
|
||||
std::string *sum;
|
||||
if (output.muxsum_op == PS_COMBINEROUTPUT_AB_CD_SUM) {
|
||||
sum = qstring_from_fmt("(%s + %s)", qstring_get_str(ab), qstring_get_str(cd));
|
||||
} else {
|
||||
sum = qstring_from_fmt("((r0.a >= 0.5) ? %s : %s)",
|
||||
qstring_get_str(cd), qstring_get_str(ab));
|
||||
}
|
||||
|
||||
std::string *sum_mapping = get_output(sum, output.mapping);
|
||||
if (sum_dest->length()) {
|
||||
qstring_append_fmt(ps->code, "%s.%s = %s(%s);\n",
|
||||
qstring_get_str(sum_dest), write_mask, caster, qstring_get_str(sum_mapping));
|
||||
}
|
||||
|
||||
delete a;
|
||||
delete b;
|
||||
delete c;
|
||||
delete d;
|
||||
delete ab;
|
||||
delete cd;
|
||||
delete ab_mapping;
|
||||
delete cd_mapping;
|
||||
delete ab_dest;
|
||||
delete cd_dest;
|
||||
delete sum_dest;
|
||||
delete sum;
|
||||
delete sum_mapping;
|
||||
}
|
||||
|
||||
// Add code for the final combiner stage
|
||||
static void add_final_stage_code(struct PixelShader *ps, struct FCInputInfo _final)
|
||||
{
|
||||
ps->varE = get_input_var(ps, _final.e, false);
|
||||
ps->varF = get_input_var(ps, _final.f, false);
|
||||
|
||||
std::string *a = get_input_var(ps, _final.a, false);
|
||||
std::string *b = get_input_var(ps, _final.b, false);
|
||||
std::string *c = get_input_var(ps, _final.c, false);
|
||||
std::string *d = get_input_var(ps, _final.d, false);
|
||||
std::string *g = get_input_var(ps, _final.g, false);
|
||||
|
||||
add_var_ref(ps, "r0");
|
||||
qstring_append_fmt(ps->code, "r0.rgb = %s + mix(vec3(%s), vec3(%s), vec3(%s));\n",
|
||||
qstring_get_str(d), qstring_get_str(c),
|
||||
qstring_get_str(b), qstring_get_str(a));
|
||||
/* FIXME: Is .x correctly here? */
|
||||
qstring_append_fmt(ps->code, "r0.a = vec3(%s).x;\n", qstring_get_str(g));
|
||||
|
||||
delete a;
|
||||
delete b;
|
||||
delete c;
|
||||
delete d;
|
||||
delete g;
|
||||
|
||||
delete ps->varE;
|
||||
delete ps->varF;
|
||||
ps->varE = ps->varF = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static std::string* psh_convert(struct PixelShader *ps)
|
||||
{
|
||||
int i;
|
||||
|
||||
std::string *preflight = new std::string();
|
||||
preflight->append(STRUCT_VERTEX_DATA);
|
||||
preflight->append("noperspective in VertexData g_vtx;\n");
|
||||
preflight->append("#define vtx g_vtx\n");
|
||||
preflight->append("\n");
|
||||
preflight->append("out vec4 fragColor;\n");
|
||||
preflight->append("\n");
|
||||
preflight->append("uniform vec4 fogColor;\n");
|
||||
|
||||
/* calculate perspective-correct inputs */
|
||||
std::string *vars = new std::string();
|
||||
vars->append("vec4 pD0 = vtx.D0 / vtx.inv_w;\n");
|
||||
vars->append("vec4 pD1 = vtx.D1 / vtx.inv_w;\n");
|
||||
vars->append("vec4 pB0 = vtx.B0 / vtx.inv_w;\n");
|
||||
vars->append("vec4 pB1 = vtx.B1 / vtx.inv_w;\n");
|
||||
vars->append("vec4 pFog = vec4(fogColor.rgb, clamp(vtx.Fog / vtx.inv_w, 0.0, 1.0));\n");
|
||||
vars->append("vec4 pT0 = vtx.T0 / vtx.inv_w;\n");
|
||||
vars->append("vec4 pT1 = vtx.T1 / vtx.inv_w;\n");
|
||||
vars->append("vec4 pT2 = vtx.T2 / vtx.inv_w;\n");
|
||||
vars->append("vec4 pT3 = vtx.T3 / vtx.inv_w;\n");
|
||||
vars->append("\n");
|
||||
vars->append("vec4 v0 = pD0;\n");
|
||||
vars->append("vec4 v1 = pD1;\n");
|
||||
|
||||
ps->code = new std::string();
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
|
||||
const char *sampler_type = NULL;
|
||||
|
||||
switch (ps->tex_modes[i]) {
|
||||
case PS_TEXTUREMODES_NONE:
|
||||
qstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_NONE */\n",
|
||||
i);
|
||||
break;
|
||||
case PS_TEXTUREMODES_PROJECT2D:
|
||||
if (ps->state.rect_tex[i]) {
|
||||
sampler_type = "sampler2DRect";
|
||||
} else {
|
||||
sampler_type = "sampler2D";
|
||||
}
|
||||
qstring_append_fmt(vars, "vec4 t%d = textureProj(texSamp%d, pT%d.xyw);\n",
|
||||
i, i, i);
|
||||
break;
|
||||
case PS_TEXTUREMODES_PROJECT3D:
|
||||
sampler_type = "sampler3D";
|
||||
qstring_append_fmt(vars, "vec4 t%d = textureProj(texSamp%d, pT%d.xyzw);\n",
|
||||
i, i, i);
|
||||
break;
|
||||
case PS_TEXTUREMODES_CUBEMAP:
|
||||
sampler_type = "samplerCube";
|
||||
qstring_append_fmt(vars, "vec4 t%d = texture(texSamp%d, pT%d.xyz / pT%d.w);\n",
|
||||
i, i, i, i);
|
||||
break;
|
||||
case PS_TEXTUREMODES_PASSTHRU:
|
||||
qstring_append_fmt(vars, "vec4 t%d = pT%d;\n", i, i);
|
||||
break;
|
||||
case PS_TEXTUREMODES_CLIPPLANE: {
|
||||
int j;
|
||||
qstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_CLIPPLANE */\n",
|
||||
i);
|
||||
for (j = 0; j < 4; j++) {
|
||||
qstring_append_fmt(vars, " if(pT%d.%c %s 0.0) { discard; };\n",
|
||||
i, "xyzw"[j],
|
||||
ps->state.compare_mode[i][j] ? ">=" : "<");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PS_TEXTUREMODES_BUMPENVMAP:
|
||||
assert(!ps->state.rect_tex[i]);
|
||||
sampler_type = "sampler2D";
|
||||
qstring_append_fmt(preflight, "uniform mat2 bumpMat%d;\n", i);
|
||||
/* FIXME: Do bumpMat swizzle on CPU before upload */
|
||||
qstring_append_fmt(vars, "vec4 t%d = texture(texSamp%d, pT%d.xy + t%d.rg * mat2(bumpMat%d[0].xy,bumpMat%d[1].yx));\n",
|
||||
i, i, i, ps->input_tex[i], i, i);
|
||||
break;
|
||||
case PS_TEXTUREMODES_BUMPENVMAP_LUM:
|
||||
qstring_append_fmt(preflight, "uniform float bumpScale%d;\n", i);
|
||||
qstring_append_fmt(preflight, "uniform float bumpOffset%d;\n", i);
|
||||
qstring_append_fmt(ps->code, "/* BUMPENVMAP_LUM for stage %d */\n", i);
|
||||
qstring_append_fmt(ps->code, "t%d = t%d * (bumpScale%d * t%d.b + bumpOffset%d);\n",
|
||||
i, i, i, ps->input_tex[i], i);
|
||||
/* Now the same as BUMPENVMAP */
|
||||
assert(!ps->state.rect_tex[i]);
|
||||
sampler_type = "sampler2D";
|
||||
qstring_append_fmt(preflight, "uniform mat2 bumpMat%d;\n", i);
|
||||
/* FIXME: Do bumpMat swizzle on CPU before upload */
|
||||
qstring_append_fmt(vars, "vec4 t%d = texture(texSamp%d, pT%d.xy + t%d.rg * mat2(bumpMat%d[0].xy,bumpMat%d[1].yx));\n",
|
||||
i, i, i, ps->input_tex[i], i, i);
|
||||
break;
|
||||
case PS_TEXTUREMODES_BRDF:
|
||||
qstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_BRDF */\n",
|
||||
i);
|
||||
assert(false); /* Unimplemented */
|
||||
break;
|
||||
case PS_TEXTUREMODES_DOT_ST:
|
||||
qstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_DOT_ST */\n",
|
||||
i);
|
||||
assert(false); /* Unimplemented */
|
||||
break;
|
||||
case PS_TEXTUREMODES_DOT_ZW:
|
||||
qstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_DOT_ZW */\n",
|
||||
i);
|
||||
assert(false); /* Unimplemented */
|
||||
break;
|
||||
case PS_TEXTUREMODES_DOT_RFLCT_DIFF:
|
||||
qstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_DOT_RFLCT_DIFF */\n",
|
||||
i);
|
||||
assert(false); /* Unimplemented */
|
||||
break;
|
||||
case PS_TEXTUREMODES_DOT_RFLCT_SPEC:
|
||||
qstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_DOT_RFLCT_SPEC */\n",
|
||||
i);
|
||||
assert(false); /* Unimplemented */
|
||||
break;
|
||||
case PS_TEXTUREMODES_DOT_STR_3D:
|
||||
qstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_DOT_STR_3D */\n",
|
||||
i);
|
||||
assert(false); /* Unimplemented */
|
||||
break;
|
||||
case PS_TEXTUREMODES_DOT_STR_CUBE:
|
||||
qstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_DOT_STR_CUBE */\n",
|
||||
i);
|
||||
assert(false); /* Unimplemented */
|
||||
break;
|
||||
case PS_TEXTUREMODES_DPNDNT_AR:
|
||||
assert(!ps->state.rect_tex[i]);
|
||||
sampler_type = "sampler2D";
|
||||
qstring_append_fmt(vars, "vec4 t%d = texture(texSamp%d, t%d.ar);\n",
|
||||
i, i, ps->input_tex[i]);
|
||||
break;
|
||||
case PS_TEXTUREMODES_DPNDNT_GB:
|
||||
assert(!ps->state.rect_tex[i]);
|
||||
sampler_type = "sampler2D";
|
||||
qstring_append_fmt(vars, "vec4 t%d = texture(texSamp%d, t%d.gb);\n",
|
||||
i, i, ps->input_tex[i]);
|
||||
break;
|
||||
case PS_TEXTUREMODES_DOTPRODUCT:
|
||||
qstring_append_fmt(vars, "vec4 t%d = vec4(dot(pT%d.xyz, t%d.rgb));\n",
|
||||
i, i, ps->input_tex[i]);
|
||||
break;
|
||||
case PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST:
|
||||
qstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST */\n",
|
||||
i);
|
||||
assert(false); /* Unimplemented */
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown ps tex mode: 0x%x\n", ps->tex_modes[i]);
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (sampler_type != NULL) {
|
||||
qstring_append_fmt(preflight, "uniform %s texSamp%d;\n", sampler_type, i);
|
||||
|
||||
/* As this means a texture fetch does happen, do alphakill */
|
||||
if (ps->state.alphakill[i]) {
|
||||
qstring_append_fmt(vars, "if (t%d.a == 0.0) { discard; };\n",
|
||||
i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ps->num_stages; i++) {
|
||||
ps->cur_stage = i;
|
||||
qstring_append_fmt(ps->code, "// Stage %d\n", i);
|
||||
add_stage_code(ps, ps->stage[i].rgb_input, ps->stage[i].rgb_output, "rgb", false);
|
||||
add_stage_code(ps, ps->stage[i].alpha_input, ps->stage[i].alpha_output, "a", true);
|
||||
}
|
||||
|
||||
if (ps->final_input.enabled) {
|
||||
ps->cur_stage = 8;
|
||||
ps->code->append("// Final Combiner\n");
|
||||
add_final_stage_code(ps, ps->final_input);
|
||||
}
|
||||
|
||||
for (i = 0; i < ps->num_var_refs; i++) {
|
||||
qstring_append_fmt(vars, "vec4 %s;\n", ps->var_refs[i]);
|
||||
if (strcmp(ps->var_refs[i], "r0") == 0) {
|
||||
if (ps->tex_modes[0] != PS_TEXTUREMODES_NONE) {
|
||||
vars->append("r0.a = t0.a;\n");
|
||||
} else {
|
||||
vars->append("r0.a = 1.0;\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < ps->num_const_refs; i++) {
|
||||
qstring_append_fmt(preflight, "uniform vec4 %s;\n", ps->const_refs[i]);
|
||||
}
|
||||
|
||||
if (ps->state.alpha_test && ps->state.alpha_func != ALPHA_FUNC_ALWAYS) {
|
||||
qstring_append_fmt(preflight, "uniform float alphaRef;\n");
|
||||
if (ps->state.alpha_func == ALPHA_FUNC_NEVER) {
|
||||
ps->code->append("discard;\n");
|
||||
} else {
|
||||
const char* alpha_op;
|
||||
switch (ps->state.alpha_func) {
|
||||
case ALPHA_FUNC_LESS: alpha_op = "<"; break;
|
||||
case ALPHA_FUNC_EQUAL: alpha_op = "=="; break;
|
||||
case ALPHA_FUNC_LEQUAL: alpha_op = "<="; break;
|
||||
case ALPHA_FUNC_GREATER: alpha_op = ">"; break;
|
||||
case ALPHA_FUNC_NOTEQUAL: alpha_op = "!="; break;
|
||||
case ALPHA_FUNC_GEQUAL: alpha_op = ">="; break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
qstring_append_fmt(ps->code, "if (!(r0.a %s alphaRef)) discard;\n",
|
||||
alpha_op);
|
||||
}
|
||||
}
|
||||
|
||||
std::string *final = new std::string();
|
||||
final->append("#version 330\n\n");
|
||||
final->append(qstring_get_str(preflight));
|
||||
final->append("void main() {\n");
|
||||
final->append(qstring_get_str(vars));
|
||||
final->append(qstring_get_str(ps->code));
|
||||
final->append("fragColor = r0;\n");
|
||||
final->append("}\n");
|
||||
|
||||
delete preflight;
|
||||
delete vars;
|
||||
delete ps->code;
|
||||
|
||||
return final;
|
||||
}
|
||||
|
||||
static void parse_input(struct InputInfo *var, int value)
|
||||
{
|
||||
var->reg = value & 0xF;
|
||||
var->chan = value & 0x10;
|
||||
var->mod = value & 0xE0;
|
||||
}
|
||||
|
||||
static void parse_combiner_inputs(uint32_t value,
|
||||
struct InputInfo *a, struct InputInfo *b,
|
||||
struct InputInfo *c, struct InputInfo *d)
|
||||
{
|
||||
parse_input(d, value & 0xFF);
|
||||
parse_input(c, (value >> 8) & 0xFF);
|
||||
parse_input(b, (value >> 16) & 0xFF);
|
||||
parse_input(a, (value >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
static void parse_combiner_output(uint32_t value, struct OutputInfo *out)
|
||||
{
|
||||
out->cd = value & 0xF;
|
||||
out->ab = (value >> 4) & 0xF;
|
||||
out->muxsum = (value >> 8) & 0xF;
|
||||
int flags = value >> 12;
|
||||
out->flags = flags;
|
||||
out->cd_op = flags & 1;
|
||||
out->ab_op = flags & 2;
|
||||
out->muxsum_op = flags & 4;
|
||||
out->mapping = flags & 0x38;
|
||||
out->ab_alphablue = flags & 0x80;
|
||||
out->cd_alphablue = flags & 0x40;
|
||||
}
|
||||
|
||||
std::string *psh_translate(const PshState state)
|
||||
{
|
||||
int i;
|
||||
struct PixelShader ps;
|
||||
memset(&ps, 0, sizeof(ps));
|
||||
|
||||
ps.state = state;
|
||||
|
||||
ps.num_stages = state.combiner_control & 0xFF;
|
||||
ps.flags = state.combiner_control >> 8;
|
||||
for (i = 0; i < 4; i++) {
|
||||
ps.tex_modes[i] = (state.shader_stage_program >> (i * 5)) & 0x1F;
|
||||
}
|
||||
|
||||
ps.input_tex[0] = -1;
|
||||
ps.input_tex[1] = 0;
|
||||
ps.input_tex[2] = (state.other_stage_input >> 16) & 0xF;
|
||||
ps.input_tex[3] = (state.other_stage_input >> 20) & 0xF;
|
||||
for (i = 0; i < ps.num_stages; i++) {
|
||||
parse_combiner_inputs(state.rgb_inputs[i],
|
||||
&ps.stage[i].rgb_input.a, &ps.stage[i].rgb_input.b,
|
||||
&ps.stage[i].rgb_input.c, &ps.stage[i].rgb_input.d);
|
||||
parse_combiner_inputs(state.alpha_inputs[i],
|
||||
&ps.stage[i].alpha_input.a, &ps.stage[i].alpha_input.b,
|
||||
&ps.stage[i].alpha_input.c, &ps.stage[i].alpha_input.d);
|
||||
|
||||
parse_combiner_output(state.rgb_outputs[i], &ps.stage[i].rgb_output);
|
||||
parse_combiner_output(state.alpha_outputs[i], &ps.stage[i].alpha_output);
|
||||
//ps.stage[i].c0 = (pDef->PSC0Mapping >> (i * 4)) & 0xF;
|
||||
//ps.stage[i].c1 = (pDef->PSC1Mapping >> (i * 4)) & 0xF;
|
||||
//ps.stage[i].c0_value = constant_0[i];
|
||||
//ps.stage[i].c1_value = constant_1[i];
|
||||
}
|
||||
|
||||
struct InputInfo blank;
|
||||
ps.final_input.enabled = state.final_inputs_0 || state.final_inputs_1;
|
||||
if (ps.final_input.enabled) {
|
||||
parse_combiner_inputs(state.final_inputs_0,
|
||||
&ps.final_input.a, &ps.final_input.b,
|
||||
&ps.final_input.c, &ps.final_input.d);
|
||||
parse_combiner_inputs(state.final_inputs_1,
|
||||
&ps.final_input.e, &ps.final_input.f,
|
||||
&ps.final_input.g, &blank);
|
||||
int flags = state.final_inputs_1 & 0xFF;
|
||||
ps.final_input.clamp_sum = flags & PS_FINALCOMBINERSETTING_CLAMP_SUM;
|
||||
ps.final_input.inv_v1 = flags & PS_FINALCOMBINERSETTING_COMPLEMENT_V1;
|
||||
ps.final_input.inv_r0 = flags & PS_FINALCOMBINERSETTING_COMPLEMENT_R0;
|
||||
//ps.final_input.c0 = (pDef->PSFinalCombinerConstants >> 0) & 0xF;
|
||||
//ps.final_input.c1 = (pDef->PSFinalCombinerConstants >> 4) & 0xF;
|
||||
//ps.final_input.c0_value = final_constant_0;
|
||||
//ps.final_input.c1_value = final_constant_1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return psh_convert(&ps);
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* QEMU Geforce NV2A pixel shader translation
|
||||
*
|
||||
* Copyright (c) 2013 espes
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
* Contributions after 2012-01-13 are licensed under the terms of the
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef HW_NV2A_PSH_H
|
||||
#define HW_NV2A_PSH_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum PshAlphaFunc {
|
||||
ALPHA_FUNC_NEVER,
|
||||
ALPHA_FUNC_LESS,
|
||||
ALPHA_FUNC_EQUAL,
|
||||
ALPHA_FUNC_LEQUAL,
|
||||
ALPHA_FUNC_GREATER,
|
||||
ALPHA_FUNC_NOTEQUAL,
|
||||
ALPHA_FUNC_GEQUAL,
|
||||
ALPHA_FUNC_ALWAYS,
|
||||
};
|
||||
|
||||
typedef struct PshState {
|
||||
/* fragment shader - register combiner stuff */
|
||||
uint32_t combiner_control;
|
||||
uint32_t shader_stage_program;
|
||||
uint32_t other_stage_input;
|
||||
uint32_t final_inputs_0;
|
||||
uint32_t final_inputs_1;
|
||||
|
||||
uint32_t rgb_inputs[8], rgb_outputs[8];
|
||||
uint32_t alpha_inputs[8], alpha_outputs[8];
|
||||
|
||||
bool rect_tex[4];
|
||||
bool compare_mode[4][4];
|
||||
bool alphakill[4];
|
||||
|
||||
bool alpha_test;
|
||||
enum PshAlphaFunc alpha_func;
|
||||
} PshState;
|
||||
|
||||
std::string *psh_translate(const PshState state);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,950 @@
|
|||
/*
|
||||
* QEMU Geforce NV2A shader generator
|
||||
*
|
||||
* Copyright (c) 2015 espes
|
||||
* Copyright (c) 2015 Jannik Vogel
|
||||
*
|
||||
* 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 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define stringify(x) xstringify(x)
|
||||
#define xstringify(x) #x
|
||||
|
||||
#include "nv2a_debug.h"
|
||||
#include "nv2a_shaders_common.h"
|
||||
#include "nv2a_shaders.h"
|
||||
|
||||
// fixme: clean this up (i'm a lazy bastard)
|
||||
#define qstring_append_fmt(str, ...) do { \
|
||||
char buf[128]; \
|
||||
snprintf(buf, sizeof(buf), __VA_ARGS__); \
|
||||
str->append(buf); \
|
||||
} while (0) \
|
||||
|
||||
#define qstring_get_str(str) str->c_str()
|
||||
|
||||
static std::string* generate_geometry_shader(
|
||||
enum ShaderPolygonMode polygon_front_mode,
|
||||
enum ShaderPolygonMode polygon_back_mode,
|
||||
enum ShaderPrimitiveMode primitive_mode,
|
||||
GLenum *gl_primitive_mode)
|
||||
{
|
||||
|
||||
/* FIXME: Missing support for 2-sided-poly mode */
|
||||
assert(polygon_front_mode == polygon_back_mode);
|
||||
enum ShaderPolygonMode polygon_mode = polygon_front_mode;
|
||||
|
||||
/* POINT mode shouldn't require any special work */
|
||||
if (polygon_mode == POLY_MODE_POINT) {
|
||||
*gl_primitive_mode = GL_POINTS;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Handle LINE and FILL mode */
|
||||
const char *layout_in = NULL;
|
||||
const char *layout_out = NULL;
|
||||
const char *body = NULL;
|
||||
switch (primitive_mode) {
|
||||
case PRIM_TYPE_POINTS: *gl_primitive_mode = GL_POINTS; return NULL;
|
||||
case PRIM_TYPE_LINES: *gl_primitive_mode = GL_LINES; return NULL;
|
||||
case PRIM_TYPE_LINE_LOOP: *gl_primitive_mode = GL_LINE_LOOP; return NULL;
|
||||
case PRIM_TYPE_LINE_STRIP: *gl_primitive_mode = GL_LINE_STRIP; return NULL;
|
||||
case PRIM_TYPE_TRIANGLES:
|
||||
*gl_primitive_mode = GL_TRIANGLES;
|
||||
if (polygon_mode == POLY_MODE_FILL) { return NULL; }
|
||||
assert(polygon_mode == POLY_MODE_LINE);
|
||||
layout_in = "layout(triangles) in;\n";
|
||||
layout_out = "layout(line_strip, max_vertices = 4) out;\n";
|
||||
body = " emit_vertex(0);\n"
|
||||
" emit_vertex(1);\n"
|
||||
" emit_vertex(2);\n"
|
||||
" emit_vertex(0);\n"
|
||||
" EndPrimitive();\n";
|
||||
break;
|
||||
case PRIM_TYPE_TRIANGLE_STRIP:
|
||||
*gl_primitive_mode = GL_TRIANGLE_STRIP;
|
||||
if (polygon_mode == POLY_MODE_FILL) { return NULL; }
|
||||
assert(polygon_mode == POLY_MODE_LINE);
|
||||
layout_in = "layout(triangles) in;\n";
|
||||
layout_out = "layout(line_strip, max_vertices = 4) out;\n";
|
||||
/* Imagine a quad made of a tristrip, the comments tell you which
|
||||
* vertex we are using */
|
||||
body = " if ((gl_PrimitiveIDIn & 1) == 0) {\n"
|
||||
" if (gl_PrimitiveIDIn == 0) {\n"
|
||||
" emit_vertex(0);\n" /* bottom right */
|
||||
" }\n"
|
||||
" emit_vertex(1);\n" /* top right */
|
||||
" emit_vertex(2);\n" /* bottom left */
|
||||
" emit_vertex(0);\n" /* bottom right */
|
||||
" } else {\n"
|
||||
" emit_vertex(2);\n" /* bottom left */
|
||||
" emit_vertex(1);\n" /* top left */
|
||||
" emit_vertex(0);\n" /* top right */
|
||||
" }\n"
|
||||
" EndPrimitive();\n";
|
||||
break;
|
||||
case PRIM_TYPE_TRIANGLE_FAN:
|
||||
*gl_primitive_mode = GL_TRIANGLE_FAN;
|
||||
if (polygon_mode == POLY_MODE_FILL) { return NULL; }
|
||||
assert(polygon_mode == POLY_MODE_LINE);
|
||||
layout_in = "layout(triangles) in;\n";
|
||||
layout_out = "layout(line_strip, max_vertices = 4) out;\n";
|
||||
body = " if (gl_PrimitiveIDIn == 0) {\n"
|
||||
" emit_vertex(0);\n"
|
||||
" }\n"
|
||||
" emit_vertex(1);\n"
|
||||
" emit_vertex(2);\n"
|
||||
" emit_vertex(0);\n"
|
||||
" EndPrimitive();\n";
|
||||
break;
|
||||
case PRIM_TYPE_QUADS:
|
||||
*gl_primitive_mode = GL_LINES_ADJACENCY;
|
||||
layout_in = "layout(lines_adjacency) in;\n";
|
||||
if (polygon_mode == POLY_MODE_LINE) {
|
||||
layout_out = "layout(line_strip, max_vertices = 5) out;\n";
|
||||
body = " emit_vertex(0);\n"
|
||||
" emit_vertex(1);\n"
|
||||
" emit_vertex(2);\n"
|
||||
" emit_vertex(3);\n"
|
||||
" emit_vertex(0);\n"
|
||||
" EndPrimitive();\n";
|
||||
} else if (polygon_mode == POLY_MODE_FILL) {
|
||||
layout_out = "layout(triangle_strip, max_vertices = 4) out;\n";
|
||||
body = " emit_vertex(0);\n"
|
||||
" emit_vertex(1);\n"
|
||||
" emit_vertex(3);\n"
|
||||
" emit_vertex(2);\n"
|
||||
" EndPrimitive();\n";
|
||||
} else {
|
||||
assert(false);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case PRIM_TYPE_QUAD_STRIP:
|
||||
*gl_primitive_mode = GL_LINE_STRIP_ADJACENCY;
|
||||
layout_in = "layout(lines_adjacency) in;\n";
|
||||
if (polygon_mode == POLY_MODE_LINE) {
|
||||
layout_out = "layout(line_strip, max_vertices = 5) out;\n";
|
||||
body = " if ((gl_PrimitiveIDIn & 1) != 0) { return; }\n"
|
||||
" if (gl_PrimitiveIDIn == 0) {\n"
|
||||
" emit_vertex(0);\n"
|
||||
" }\n"
|
||||
" emit_vertex(1);\n"
|
||||
" emit_vertex(3);\n"
|
||||
" emit_vertex(2);\n"
|
||||
" emit_vertex(0);\n"
|
||||
" EndPrimitive();\n";
|
||||
} else if (polygon_mode == POLY_MODE_FILL) {
|
||||
layout_out = "layout(triangle_strip, max_vertices = 4) out;\n";
|
||||
body = " if ((gl_PrimitiveIDIn & 1) != 0) { return; }\n"
|
||||
" emit_vertex(0);\n"
|
||||
" emit_vertex(1);\n"
|
||||
" emit_vertex(2);\n"
|
||||
" emit_vertex(3);\n"
|
||||
" EndPrimitive();\n";
|
||||
} else {
|
||||
assert(false);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case PRIM_TYPE_POLYGON:
|
||||
if (polygon_mode == POLY_MODE_LINE) {
|
||||
*gl_primitive_mode = GL_LINE_LOOP;
|
||||
} else if (polygon_mode == POLY_MODE_FILL) {
|
||||
*gl_primitive_mode = GL_TRIANGLE_FAN;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
return NULL;
|
||||
default:
|
||||
assert(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* generate a geometry shader to support deprecated primitive types */
|
||||
assert(layout_in);
|
||||
assert(layout_out);
|
||||
assert(body);
|
||||
std::string* s = new std::string("#version 330\n"
|
||||
"\n");
|
||||
s->append(layout_in);
|
||||
s->append(layout_out);
|
||||
s->append("\n"
|
||||
STRUCT_VERTEX_DATA
|
||||
"noperspective in VertexData v_vtx[];\n"
|
||||
"noperspective out VertexData g_vtx;\n"
|
||||
"\n"
|
||||
"void emit_vertex(int index) {\n"
|
||||
" gl_Position = gl_in[index].gl_Position;\n"
|
||||
" gl_PointSize = gl_in[index].gl_PointSize;\n"
|
||||
" g_vtx = v_vtx[index];\n"
|
||||
" EmitVertex();\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"void main() {\n");
|
||||
s->append(body);
|
||||
s->append("}\n");
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void append_skinning_code(std::string* str, bool mix,
|
||||
unsigned int count, const char* type,
|
||||
const char* output, const char* input,
|
||||
const char* matrix, const char* swizzle)
|
||||
{
|
||||
|
||||
if (count == 0) {
|
||||
qstring_append_fmt(str, "%s %s = (%s * %s0).%s;\n",
|
||||
type, output, input, matrix, swizzle);
|
||||
} else {
|
||||
qstring_append_fmt(str, "%s %s = %s(0.0);\n", type, output, type);
|
||||
if (mix) {
|
||||
/* Tweening */
|
||||
if (count == 2) {
|
||||
qstring_append_fmt(str,
|
||||
"%s += mix((%s * %s1).%s,\n"
|
||||
" (%s * %s0).%s, weight.x);\n",
|
||||
output,
|
||||
input, matrix, swizzle,
|
||||
input, matrix, swizzle);
|
||||
} else {
|
||||
/* FIXME: Not sure how blend weights are calculated */
|
||||
assert(false);
|
||||
}
|
||||
} else {
|
||||
/* Individual matrices */
|
||||
unsigned int i;
|
||||
for (i = 0; i < count; i++) {
|
||||
char c = "xyzw"[i];
|
||||
qstring_append_fmt(str, "%s += (%s * %s%d * weight.%c).%s;\n",
|
||||
output, input, matrix, i, c,
|
||||
swizzle);
|
||||
}
|
||||
assert(false); /* FIXME: Untested */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define GLSL_C(idx) "c[" stringify(idx) "]"
|
||||
#define GLSL_LTCTXA(idx) "ltctxa[" stringify(idx) "]"
|
||||
|
||||
#define GLSL_C_MAT4(idx) \
|
||||
"mat4(" GLSL_C(idx) ", " GLSL_C(idx+1) ", " \
|
||||
GLSL_C(idx+2) ", " GLSL_C(idx+3) ")"
|
||||
|
||||
#define GLSL_DEFINE(a, b) "#define " stringify(a) " " b "\n"
|
||||
|
||||
static void generate_fixed_function(const ShaderState state,
|
||||
std::string *header, std::string *body)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
/* generate vertex shader mimicking fixed function */
|
||||
header->append("#define position v0\n"
|
||||
"#define weight v1\n"
|
||||
"#define normal v2.xyz\n"
|
||||
"#define diffuse v3\n"
|
||||
"#define specular v4\n"
|
||||
"#define fogCoord v5.x\n"
|
||||
"#define pointSize v6\n"
|
||||
"#define backDiffuse v7\n"
|
||||
"#define backSpecular v8\n"
|
||||
"#define texture0 v9\n"
|
||||
"#define texture1 v10\n"
|
||||
"#define texture2 v11\n"
|
||||
"#define texture3 v12\n"
|
||||
"#define reserved1 v13\n"
|
||||
"#define reserved2 v14\n"
|
||||
"#define reserved3 v15\n"
|
||||
"\n"
|
||||
"uniform vec4 ltctxa[" stringify(NV2A_LTCTXA_COUNT) "];\n"
|
||||
"uniform vec4 ltctxb[" stringify(NV2A_LTCTXB_COUNT) "];\n"
|
||||
"uniform vec4 ltc1[" stringify(NV2A_LTC1_COUNT) "];\n"
|
||||
"\n"
|
||||
GLSL_DEFINE(projectionMat, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_PMAT0))
|
||||
GLSL_DEFINE(compositeMat, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_CMAT0))
|
||||
"\n"
|
||||
GLSL_DEFINE(texPlaneS0, GLSL_C(NV_IGRAPH_XF_XFCTX_TG0MAT + 0))
|
||||
GLSL_DEFINE(texPlaneT0, GLSL_C(NV_IGRAPH_XF_XFCTX_TG0MAT + 1))
|
||||
GLSL_DEFINE(texPlaneQ0, GLSL_C(NV_IGRAPH_XF_XFCTX_TG0MAT + 2))
|
||||
GLSL_DEFINE(texPlaneR0, GLSL_C(NV_IGRAPH_XF_XFCTX_TG0MAT + 3))
|
||||
"\n"
|
||||
GLSL_DEFINE(texPlaneS1, GLSL_C(NV_IGRAPH_XF_XFCTX_TG1MAT + 0))
|
||||
GLSL_DEFINE(texPlaneT1, GLSL_C(NV_IGRAPH_XF_XFCTX_TG1MAT + 1))
|
||||
GLSL_DEFINE(texPlaneQ1, GLSL_C(NV_IGRAPH_XF_XFCTX_TG1MAT + 2))
|
||||
GLSL_DEFINE(texPlaneR1, GLSL_C(NV_IGRAPH_XF_XFCTX_TG1MAT + 3))
|
||||
"\n"
|
||||
GLSL_DEFINE(texPlaneS2, GLSL_C(NV_IGRAPH_XF_XFCTX_TG2MAT + 0))
|
||||
GLSL_DEFINE(texPlaneT2, GLSL_C(NV_IGRAPH_XF_XFCTX_TG2MAT + 1))
|
||||
GLSL_DEFINE(texPlaneQ2, GLSL_C(NV_IGRAPH_XF_XFCTX_TG2MAT + 2))
|
||||
GLSL_DEFINE(texPlaneR2, GLSL_C(NV_IGRAPH_XF_XFCTX_TG2MAT + 3))
|
||||
"\n"
|
||||
GLSL_DEFINE(texPlaneS3, GLSL_C(NV_IGRAPH_XF_XFCTX_TG3MAT + 0))
|
||||
GLSL_DEFINE(texPlaneT3, GLSL_C(NV_IGRAPH_XF_XFCTX_TG3MAT + 1))
|
||||
GLSL_DEFINE(texPlaneQ3, GLSL_C(NV_IGRAPH_XF_XFCTX_TG3MAT + 2))
|
||||
GLSL_DEFINE(texPlaneR3, GLSL_C(NV_IGRAPH_XF_XFCTX_TG3MAT + 3))
|
||||
"\n"
|
||||
GLSL_DEFINE(modelViewMat0, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_MMAT0))
|
||||
GLSL_DEFINE(modelViewMat1, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_MMAT1))
|
||||
GLSL_DEFINE(modelViewMat2, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_MMAT2))
|
||||
GLSL_DEFINE(modelViewMat3, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_MMAT3))
|
||||
"\n"
|
||||
GLSL_DEFINE(invModelViewMat0, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_IMMAT0))
|
||||
GLSL_DEFINE(invModelViewMat1, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_IMMAT1))
|
||||
GLSL_DEFINE(invModelViewMat2, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_IMMAT2))
|
||||
GLSL_DEFINE(invModelViewMat3, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_IMMAT3))
|
||||
"\n"
|
||||
GLSL_DEFINE(eyePosition, GLSL_C(NV_IGRAPH_XF_XFCTX_EYEP))
|
||||
"\n"
|
||||
"#define lightAmbientColor(i) "
|
||||
"ltctxb[" stringify(NV_IGRAPH_XF_LTCTXB_L0_AMB) " + (i)*6].xyz\n"
|
||||
"#define lightDiffuseColor(i) "
|
||||
"ltctxb[" stringify(NV_IGRAPH_XF_LTCTXB_L0_DIF) " + (i)*6].xyz\n"
|
||||
"#define lightSpecularColor(i) "
|
||||
"ltctxb[" stringify(NV_IGRAPH_XF_LTCTXB_L0_SPC) " + (i)*6].xyz\n"
|
||||
"\n"
|
||||
"#define lightSpotFalloff(i) "
|
||||
"ltctxa[" stringify(NV_IGRAPH_XF_LTCTXA_L0_K) " + (i)*2].xyz\n"
|
||||
"#define lightSpotDirection(i) "
|
||||
"ltctxa[" stringify(NV_IGRAPH_XF_LTCTXA_L0_SPT) " + (i)*2]\n"
|
||||
"\n"
|
||||
"#define lightLocalRange(i) "
|
||||
"ltc1[" stringify(NV_IGRAPH_XF_LTC1_r0) " + (i)].x\n"
|
||||
"\n"
|
||||
GLSL_DEFINE(sceneAmbientColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_FR_AMB) ".xyz")
|
||||
"\n"
|
||||
"uniform mat4 invViewport;\n"
|
||||
"\n");
|
||||
|
||||
/* Skinning */
|
||||
unsigned int count;
|
||||
bool mix;
|
||||
switch (state.skinning) {
|
||||
case SKINNING_OFF:
|
||||
mix = false; count = 0; break;
|
||||
case SKINNING_1WEIGHTS:
|
||||
mix = true; count = 2; break;
|
||||
case SKINNING_2WEIGHTS:
|
||||
mix = true; count = 3; break;
|
||||
case SKINNING_3WEIGHTS:
|
||||
mix = true; count = 4; break;
|
||||
case SKINNING_2WEIGHTS2MATRICES:
|
||||
mix = false; count = 2; break;
|
||||
case SKINNING_3WEIGHTS3MATRICES:
|
||||
mix = false; count = 3; break;
|
||||
case SKINNING_4WEIGHTS4MATRICES:
|
||||
mix = false; count = 4; break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
qstring_append_fmt(body, "/* Skinning mode %d */\n",
|
||||
state.skinning);
|
||||
|
||||
append_skinning_code(body, mix, count, "vec4",
|
||||
"tPosition", "position",
|
||||
"modelViewMat", "xyzw");
|
||||
append_skinning_code(body, mix, count, "vec3",
|
||||
"tNormal", "vec4(normal, 0.0)",
|
||||
"invModelViewMat", "xyz");
|
||||
|
||||
/* Normalization */
|
||||
if (state.normalization) {
|
||||
body->append("tNormal = normalize(tNormal);\n");
|
||||
}
|
||||
|
||||
/* Texgen */
|
||||
for (i = 0; i < NV2A_MAX_TEXTURES; i++) {
|
||||
qstring_append_fmt(body, "/* Texgen for stage %d */\n",
|
||||
i);
|
||||
/* Set each component individually */
|
||||
/* FIXME: could be nicer if some channels share the same texgen */
|
||||
for (j = 0; j < 4; j++) {
|
||||
/* TODO: TexGen View Model missing! */
|
||||
char c = "xyzw"[j];
|
||||
char cSuffix = "STRQ"[j];
|
||||
switch (state.texgen[i][j]) {
|
||||
case TEXGEN_DISABLE:
|
||||
qstring_append_fmt(body, "oT%d.%c = texture%d.%c;\n",
|
||||
i, c, i, c);
|
||||
break;
|
||||
case TEXGEN_EYE_LINEAR:
|
||||
qstring_append_fmt(body, "oT%d.%c = dot(texPlane%c%d, tPosition);\n",
|
||||
i, c, cSuffix, i);
|
||||
break;
|
||||
case TEXGEN_OBJECT_LINEAR:
|
||||
qstring_append_fmt(body, "oT%d.%c = dot(texPlane%c%d, position);\n",
|
||||
i, c, cSuffix, i);
|
||||
assert(false); /* Untested */
|
||||
break;
|
||||
case TEXGEN_SPHERE_MAP:
|
||||
assert(i < 2); /* Channels S,T only! */
|
||||
body->append("{\n");
|
||||
/* FIXME: u, r and m only have to be calculated once */
|
||||
body->append(" vec3 u = normalize(tPosition.xyz);\n");
|
||||
//FIXME: tNormal before or after normalization? Always normalize?
|
||||
body->append(" vec3 r = reflect(u, tNormal);\n");
|
||||
|
||||
/* FIXME: This would consume 1 division fewer and *might* be
|
||||
* faster than length:
|
||||
* // [z=1/(2*x) => z=1/x*0.5]
|
||||
* vec3 ro = r + vec3(0.0, 0.0, 1.0);
|
||||
* float m = inversesqrt(dot(ro,ro))*0.5;
|
||||
*/
|
||||
|
||||
body->append(" float invM = 1.0 / (2.0 * length(r + vec3(0.0, 0.0, 1.0)));\n");
|
||||
qstring_append_fmt(body, " oT%d.%c = r.%c * invM + 0.5;\n",
|
||||
i, c, c);
|
||||
body->append("}\n");
|
||||
assert(false); /* Untested */
|
||||
break;
|
||||
case TEXGEN_REFLECTION_MAP:
|
||||
assert(i < 3); /* Channels S,T,R only! */
|
||||
body->append("{\n");
|
||||
/* FIXME: u and r only have to be calculated once, can share the one from SPHERE_MAP */
|
||||
body->append(" vec3 u = normalize(tPosition.xyz);\n");
|
||||
body->append(" vec3 r = reflect(u, tNormal);\n");
|
||||
qstring_append_fmt(body, " oT%d.%c = r.%c;\n",
|
||||
i, c, c);
|
||||
body->append("}\n");
|
||||
break;
|
||||
case TEXGEN_NORMAL_MAP:
|
||||
assert(i < 3); /* Channels S,T,R only! */
|
||||
qstring_append_fmt(body, "oT%d.%c = tNormal.%c;\n",
|
||||
i, c, c);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply texture matrices */
|
||||
for (i = 0; i < NV2A_MAX_TEXTURES; i++) {
|
||||
if (state.texture_matrix_enable[i]) {
|
||||
qstring_append_fmt(body,
|
||||
"oT%d = oT%d * texMat%d;\n",
|
||||
i, i, i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Lighting */
|
||||
if (state.lighting) {
|
||||
|
||||
//FIXME: Do 2 passes if we want 2 sided-lighting?
|
||||
body->append("oD0 = vec4(sceneAmbientColor, diffuse.a);\n");
|
||||
body->append("oD1 = vec4(0.0, 0.0, 0.0, specular.a);\n");
|
||||
|
||||
for (i = 0; i < NV2A_MAX_LIGHTS; i++) {
|
||||
if (state.light[i] == LIGHT_OFF) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* FIXME: It seems that we only have to handle the surface colors if
|
||||
* they are not part of the material [= vertex colors].
|
||||
* If they are material the cpu will premultiply light
|
||||
* colors
|
||||
*/
|
||||
|
||||
qstring_append_fmt(body, "/* Light %d */ {\n", i);
|
||||
|
||||
if (state.light[i] == LIGHT_LOCAL
|
||||
|| state.light[i] == LIGHT_SPOT) {
|
||||
|
||||
qstring_append_fmt(header,
|
||||
"uniform vec3 lightLocalPosition%d;\n"
|
||||
"uniform vec3 lightLocalAttenuation%d;\n",
|
||||
i, i);
|
||||
qstring_append_fmt(body,
|
||||
" vec3 VP = lightLocalPosition%d - tPosition.xyz/tPosition.w;\n"
|
||||
" float d = length(VP);\n"
|
||||
//FIXME: if (d > lightLocalRange) { .. don't process this light .. } /* inclusive?! */ - what about directional lights?
|
||||
" VP = normalize(VP);\n"
|
||||
" float attenuation = 1.0 / (lightLocalAttenuation%d.x\n"
|
||||
" + lightLocalAttenuation%d.y * d\n"
|
||||
" + lightLocalAttenuation%d.z * d * d);\n"
|
||||
" vec3 halfVector = normalize(VP + eyePosition.xyz / eyePosition.w);\n" /* FIXME: Not sure if eyePosition is correct */
|
||||
" float nDotVP = max(0.0, dot(tNormal, VP));\n"
|
||||
" float nDotHV = max(0.0, dot(tNormal, halfVector));\n",
|
||||
i, i, i, i);
|
||||
|
||||
}
|
||||
|
||||
switch(state.light[i]) {
|
||||
case LIGHT_INFINITE:
|
||||
|
||||
/* lightLocalRange will be 1e+30 here */
|
||||
|
||||
qstring_append_fmt(header,
|
||||
"uniform vec3 lightInfiniteHalfVector%d;\n"
|
||||
"uniform vec3 lightInfiniteDirection%d;\n",
|
||||
i, i);
|
||||
qstring_append_fmt(body,
|
||||
" float attenuation = 1.0;\n"
|
||||
" float nDotVP = max(0.0, dot(tNormal, normalize(vec3(lightInfiniteDirection%d))));\n"
|
||||
" float nDotHV = max(0.0, dot(tNormal, vec3(lightInfiniteHalfVector%d)));\n",
|
||||
i, i);
|
||||
|
||||
/* FIXME: Do specular */
|
||||
|
||||
/* FIXME: tBackDiffuse */
|
||||
|
||||
break;
|
||||
case LIGHT_LOCAL:
|
||||
/* Everything done already */
|
||||
break;
|
||||
case LIGHT_SPOT:
|
||||
assert(false);
|
||||
/*FIXME: calculate falloff */
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
qstring_append_fmt(body,
|
||||
" float pf;\n"
|
||||
" if (nDotVP == 0.0) {\n"
|
||||
" pf = 0.0;\n"
|
||||
" } else {\n"
|
||||
" pf = pow(nDotHV, /* specular(l, m, n, l1, m1, n1) */ 0.001);\n"
|
||||
" }\n"
|
||||
" vec3 lightAmbient = lightAmbientColor(%d) * attenuation;\n"
|
||||
" vec3 lightDiffuse = lightDiffuseColor(%d) * attenuation * nDotVP;\n"
|
||||
" vec3 lightSpecular = lightSpecularColor(%d) * pf;\n",
|
||||
i, i, i);
|
||||
|
||||
body->append(" oD0.xyz += lightAmbient;\n");
|
||||
body->append(" oD0.xyz += diffuse.xyz * lightDiffuse;\n");
|
||||
body->append(" oD1.xyz += specular.xyz * lightSpecular;\n");
|
||||
body->append("}\n");
|
||||
}
|
||||
} else {
|
||||
body->append(" oD0 = diffuse;\n");
|
||||
body->append(" oD1 = specular;\n");
|
||||
}
|
||||
body->append(" oB0 = backDiffuse;\n");
|
||||
body->append(" oB1 = backSpecular;\n");
|
||||
|
||||
/* Fog */
|
||||
if (state.fog_enable) {
|
||||
|
||||
/* From: https://www.opengl.org/registry/specs/NV/fog_distance.txt */
|
||||
switch(state.foggen) {
|
||||
case FOGGEN_SPEC_ALPHA:
|
||||
/* FIXME: Do we have to clamp here? */
|
||||
body->append(" float fogDistance = clamp(specular.a, 0.0, 1.0);\n");
|
||||
break;
|
||||
case FOGGEN_RADIAL:
|
||||
body->append(" float fogDistance = length(tPosition.xyz);\n");
|
||||
break;
|
||||
case FOGGEN_PLANAR:
|
||||
case FOGGEN_ABS_PLANAR:
|
||||
body->append(" float fogDistance = dot(fogPlane.xyz, tPosition.xyz) + fogPlane.w;\n");
|
||||
if (state.foggen == FOGGEN_ABS_PLANAR) {
|
||||
body->append(" fogDistance = abs(fogDistance);\n");
|
||||
}
|
||||
break;
|
||||
case FOGGEN_FOG_X:
|
||||
body->append(" float fogDistance = fogCoord;\n");
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* If skinning is off the composite matrix already includes the MV matrix */
|
||||
if (state.skinning == SKINNING_OFF) {
|
||||
body->append(" tPosition = position;\n");
|
||||
}
|
||||
|
||||
body->append(
|
||||
" oPos = invViewport * (tPosition * compositeMat);\n"
|
||||
" oPos.z = oPos.z * 2.0 - oPos.w;\n");
|
||||
|
||||
body->append(" vtx.inv_w = 1.0 / oPos.w;\n");
|
||||
|
||||
}
|
||||
|
||||
static std::string *generate_vertex_shader(const ShaderState state,
|
||||
char vtx_prefix)
|
||||
{
|
||||
int i;
|
||||
std::string *header = new std::string(
|
||||
"#version 330\n"
|
||||
"\n"
|
||||
"uniform vec2 clipRange;\n"
|
||||
"uniform vec2 surfaceSize;\n"
|
||||
"\n"
|
||||
/* All constants in 1 array declaration */
|
||||
"uniform vec4 c[" stringify(NV2A_VERTEXSHADER_CONSTANTS) "];\n"
|
||||
"\n"
|
||||
"uniform vec4 fogColor;\n"
|
||||
"uniform float fogParam[2];\n"
|
||||
"\n"
|
||||
|
||||
GLSL_DEFINE(fogPlane, GLSL_C(NV_IGRAPH_XF_XFCTX_FOG))
|
||||
GLSL_DEFINE(texMat0, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_T0MAT))
|
||||
GLSL_DEFINE(texMat1, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_T1MAT))
|
||||
GLSL_DEFINE(texMat2, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_T2MAT))
|
||||
GLSL_DEFINE(texMat3, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_T3MAT))
|
||||
|
||||
"\n"
|
||||
"vec4 oPos = vec4(0.0,0.0,0.0,1.0);\n"
|
||||
"vec4 oD0 = vec4(0.0,0.0,0.0,1.0);\n"
|
||||
"vec4 oD1 = vec4(0.0,0.0,0.0,1.0);\n"
|
||||
"vec4 oB0 = vec4(0.0,0.0,0.0,1.0);\n"
|
||||
"vec4 oB1 = vec4(0.0,0.0,0.0,1.0);\n"
|
||||
"vec4 oPts = vec4(0.0,0.0,0.0,1.0);\n"
|
||||
/* FIXME: NV_vertex_program says: "FOGC is the transformed vertex's fog
|
||||
* coordinate. The register's first floating-point component is interpolated
|
||||
* across the assembled primitive during rasterization and used as the fog
|
||||
* distance to compute per-fragment the fog factor when fog is enabled.
|
||||
* However, if both fog and vertex program mode are enabled, but the FOGC
|
||||
* vertex result register is not written, the fog factor is overridden to
|
||||
* 1.0. The register's other three components are ignored."
|
||||
*
|
||||
* That probably means it will read back as vec4(0.0, 0.0, 0.0, 1.0) but
|
||||
* will be set to 1.0 AFTER the VP if it was never written?
|
||||
* We should test on real hardware..
|
||||
*
|
||||
* We'll force 1.0 for oFog.x for now.
|
||||
*/
|
||||
"vec4 oFog = vec4(1.0,0.0,0.0,1.0);\n"
|
||||
"vec4 oT0 = vec4(0.0,0.0,0.0,1.0);\n"
|
||||
"vec4 oT1 = vec4(0.0,0.0,0.0,1.0);\n"
|
||||
"vec4 oT2 = vec4(0.0,0.0,0.0,1.0);\n"
|
||||
"vec4 oT3 = vec4(0.0,0.0,0.0,1.0);\n"
|
||||
"\n"
|
||||
STRUCT_VERTEX_DATA);
|
||||
|
||||
qstring_append_fmt(header, "noperspective out VertexData %c_vtx;\n",
|
||||
vtx_prefix);
|
||||
qstring_append_fmt(header, "#define vtx %c_vtx\n",
|
||||
vtx_prefix);
|
||||
header->append("\n");
|
||||
for(i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) {
|
||||
qstring_append_fmt(header, "in vec4 v%d;\n", i);
|
||||
}
|
||||
header->append("\n");
|
||||
|
||||
std::string *body = new std::string("void main() {\n");
|
||||
|
||||
if (state.fixed_function) {
|
||||
generate_fixed_function(state, header, body);
|
||||
|
||||
} else if (state.vertex_program) {
|
||||
vsh_translate(VSH_VERSION_XVS,
|
||||
(uint32_t*)state.program_data,
|
||||
state.program_length,
|
||||
state.z_perspective,
|
||||
header, body);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
|
||||
/* Fog */
|
||||
|
||||
if (state.fog_enable) {
|
||||
|
||||
if (state.vertex_program) {
|
||||
/* FIXME: Does foggen do something here? Let's do some tracking..
|
||||
*
|
||||
* "RollerCoaster Tycoon" has
|
||||
* state.vertex_program = true; state.foggen == FOGGEN_PLANAR
|
||||
* but expects oFog.x as fogdistance?! Writes oFog.xyzw = v0.z
|
||||
*/
|
||||
body->append(" float fogDistance = oFog.x;\n");
|
||||
}
|
||||
|
||||
/* FIXME: Do this per pixel? */
|
||||
|
||||
switch (state.fog_mode) {
|
||||
case FOG_MODE_LINEAR:
|
||||
case FOG_MODE_LINEAR_ABS:
|
||||
|
||||
/* f = (end - d) / (end - start)
|
||||
* fogParam[1] = 1 / (end - start)
|
||||
* fogParam[0] = 1 + end * fogParam[1];
|
||||
*/
|
||||
|
||||
body->append(" float fogFactor = fogParam[0] + fogDistance * fogParam[1];\n");
|
||||
body->append(" fogFactor -= 1.0;\n"); /* FIXME: WHHYYY?!! */
|
||||
break;
|
||||
case FOG_MODE_EXP:
|
||||
case FOG_MODE_EXP_ABS:
|
||||
|
||||
/* f = 1 / (e^(d * density))
|
||||
* fogParam[1] = -density / (2 * ln(256))
|
||||
* fogParam[0] = 1.5
|
||||
*/
|
||||
|
||||
body->append(" float fogFactor = fogParam[0] + exp2(fogDistance * fogParam[1] * 16.0);\n");
|
||||
body->append(" fogFactor -= 1.5;\n"); /* FIXME: WHHYYY?!! */
|
||||
break;
|
||||
case FOG_MODE_EXP2:
|
||||
case FOG_MODE_EXP2_ABS:
|
||||
|
||||
/* f = 1 / (e^((d * density)^2))
|
||||
* fogParam[1] = -density / (2 * sqrt(ln(256)))
|
||||
* fogParam[0] = 1.5
|
||||
*/
|
||||
|
||||
body->append(" float fogFactor = fogParam[0] + exp2(-fogDistance * fogDistance * fogParam[1] * fogParam[1] * 32.0);\n");
|
||||
body->append(" fogFactor -= 1.5;\n"); /* FIXME: WHHYYY?!! */
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
/* Calculate absolute for the modes which need it */
|
||||
switch (state.fog_mode) {
|
||||
case FOG_MODE_LINEAR_ABS:
|
||||
case FOG_MODE_EXP_ABS:
|
||||
case FOG_MODE_EXP2_ABS:
|
||||
body->append(" fogFactor = abs(fogFactor);\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* FIXME: What about fog alpha?! */
|
||||
body->append(" oFog.xyzw = vec4(fogFactor);\n");
|
||||
} else {
|
||||
/* FIXME: Is the fog still calculated / passed somehow?!
|
||||
*/
|
||||
body->append(" oFog.xyzw = vec4(1.0);\n");
|
||||
}
|
||||
|
||||
/* Set outputs */
|
||||
body->append("\n"
|
||||
" vtx.D0 = clamp(oD0, 0.0, 1.0) * vtx.inv_w;\n"
|
||||
" vtx.D1 = clamp(oD1, 0.0, 1.0) * vtx.inv_w;\n"
|
||||
" vtx.B0 = clamp(oB0, 0.0, 1.0) * vtx.inv_w;\n"
|
||||
" vtx.B1 = clamp(oB1, 0.0, 1.0) * vtx.inv_w;\n"
|
||||
" vtx.Fog = oFog.x * vtx.inv_w;\n"
|
||||
" vtx.T0 = oT0 * vtx.inv_w;\n"
|
||||
" vtx.T1 = oT1 * vtx.inv_w;\n"
|
||||
" vtx.T2 = oT2 * vtx.inv_w;\n"
|
||||
" vtx.T3 = oT3 * vtx.inv_w;\n"
|
||||
" gl_Position = oPos;\n"
|
||||
" gl_PointSize = oPts.x;\n"
|
||||
"\n"
|
||||
"}\n");
|
||||
|
||||
|
||||
/* Return combined header + source */
|
||||
header->append(qstring_get_str(body));
|
||||
delete body;
|
||||
return header;
|
||||
}
|
||||
|
||||
static GLuint create_gl_shader(GLenum gl_shader_type,
|
||||
const char *code,
|
||||
const char *name)
|
||||
{
|
||||
GLint compiled = 0;
|
||||
|
||||
NV2A_GL_DGROUP_BEGIN("Creating new %s", name);
|
||||
|
||||
NV2A_DPRINTF("compile new %s, code:\n%s\n", name, code);
|
||||
|
||||
GLuint shader = glCreateShader(gl_shader_type);
|
||||
glShaderSource(shader, 1, &code, 0);
|
||||
glCompileShader(shader);
|
||||
|
||||
/* Check it compiled */
|
||||
compiled = 0;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
|
||||
if (!compiled) {
|
||||
GLchar* log;
|
||||
GLint log_length;
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
|
||||
log = (GLchar*)malloc(log_length * sizeof(GLchar));
|
||||
glGetShaderInfoLog(shader, log_length, NULL, log);
|
||||
fprintf(stderr, "nv2a: %s compilation failed: %s\n", name, log);
|
||||
free(log);
|
||||
|
||||
NV2A_GL_DGROUP_END();
|
||||
abort();
|
||||
}
|
||||
|
||||
NV2A_GL_DGROUP_END();
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
ShaderBinding* generate_shaders(const ShaderState state)
|
||||
{
|
||||
int i, j;
|
||||
char tmp[64];
|
||||
|
||||
char vtx_prefix;
|
||||
GLuint program = glCreateProgram();
|
||||
|
||||
/* Create an option geometry shader and find primitive type */
|
||||
|
||||
GLenum gl_primitive_mode;
|
||||
std::string* geometry_shader_code =
|
||||
generate_geometry_shader(state.polygon_front_mode,
|
||||
state.polygon_back_mode,
|
||||
state.primitive_mode,
|
||||
&gl_primitive_mode);
|
||||
if (geometry_shader_code) {
|
||||
const char* geometry_shader_code_str =
|
||||
qstring_get_str(geometry_shader_code);
|
||||
|
||||
GLuint geometry_shader = create_gl_shader(GL_GEOMETRY_SHADER,
|
||||
geometry_shader_code_str,
|
||||
"geometry shader");
|
||||
glAttachShader(program, geometry_shader);
|
||||
|
||||
delete geometry_shader_code;
|
||||
|
||||
vtx_prefix = 'v';
|
||||
} else {
|
||||
vtx_prefix = 'g';
|
||||
}
|
||||
|
||||
/* create the vertex shader */
|
||||
|
||||
std::string *vertex_shader_code = generate_vertex_shader(state, vtx_prefix);
|
||||
GLuint vertex_shader = create_gl_shader(GL_VERTEX_SHADER,
|
||||
qstring_get_str(vertex_shader_code),
|
||||
"vertex shader");
|
||||
glAttachShader(program, vertex_shader);
|
||||
delete vertex_shader_code;
|
||||
|
||||
|
||||
/* Bind attributes for vertices */
|
||||
for(i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) {
|
||||
snprintf(tmp, sizeof(tmp), "v%d", i);
|
||||
glBindAttribLocation(program, i, tmp);
|
||||
}
|
||||
|
||||
|
||||
/* generate a fragment shader from register combiners */
|
||||
|
||||
std::string *fragment_shader_code = psh_translate(state.psh);
|
||||
|
||||
const char *fragment_shader_code_str = qstring_get_str(fragment_shader_code);
|
||||
|
||||
GLuint fragment_shader = create_gl_shader(GL_FRAGMENT_SHADER,
|
||||
fragment_shader_code_str,
|
||||
"fragment shader");
|
||||
glAttachShader(program, fragment_shader);
|
||||
|
||||
delete fragment_shader_code;
|
||||
|
||||
|
||||
/* link the program */
|
||||
glLinkProgram(program);
|
||||
GLint linked = 0;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &linked);
|
||||
if(!linked) {
|
||||
GLchar log[2048];
|
||||
glGetProgramInfoLog(program, 2048, NULL, log);
|
||||
fprintf(stderr, "nv2a: shader linking failed: %s\n", log);
|
||||
abort();
|
||||
}
|
||||
|
||||
glUseProgram(program);
|
||||
|
||||
/* set texture samplers */
|
||||
for (i = 0; i < NV2A_MAX_TEXTURES; i++) {
|
||||
char samplerName[16];
|
||||
snprintf(samplerName, sizeof(samplerName), "texSamp%d", i);
|
||||
GLint texSampLoc = glGetUniformLocation(program, samplerName);
|
||||
if (texSampLoc >= 0) {
|
||||
glUniform1i(texSampLoc, i);
|
||||
}
|
||||
}
|
||||
|
||||
/* validate the program */
|
||||
glValidateProgram(program);
|
||||
GLint valid = 0;
|
||||
glGetProgramiv(program, GL_VALIDATE_STATUS, &valid);
|
||||
if (!valid) {
|
||||
GLchar log[1024];
|
||||
glGetProgramInfoLog(program, 1024, NULL, log);
|
||||
fprintf(stderr, "nv2a: shader validation failed: %s\n", log);
|
||||
abort();
|
||||
}
|
||||
|
||||
ShaderBinding* ret = (ShaderBinding*)calloc(1, sizeof(ShaderBinding));
|
||||
ret->gl_program = program;
|
||||
ret->gl_primitive_mode = gl_primitive_mode;
|
||||
|
||||
/* lookup fragment shader uniforms */
|
||||
for (i=0; i<=8; i++) {
|
||||
for (j=0; j<2; j++) {
|
||||
snprintf(tmp, sizeof(tmp), "c_%d_%d", i, j);
|
||||
ret->psh_constant_loc[i][j] = glGetUniformLocation(program, tmp);
|
||||
}
|
||||
}
|
||||
ret->alpha_ref_loc = glGetUniformLocation(program, "alphaRef");
|
||||
for (i = 1; i < NV2A_MAX_TEXTURES; i++) {
|
||||
snprintf(tmp, sizeof(tmp), "bumpMat%d", i);
|
||||
ret->bump_mat_loc[i] = glGetUniformLocation(program, tmp);
|
||||
snprintf(tmp, sizeof(tmp), "bumpScale%d", i);
|
||||
ret->bump_scale_loc[i] = glGetUniformLocation(program, tmp);
|
||||
snprintf(tmp, sizeof(tmp), "bumpOffset%d", i);
|
||||
ret->bump_offset_loc[i] = glGetUniformLocation(program, tmp);
|
||||
}
|
||||
|
||||
/* lookup vertex shader uniforms */
|
||||
for(i = 0; i < NV2A_VERTEXSHADER_CONSTANTS; i++) {
|
||||
snprintf(tmp, sizeof(tmp), "c[%d]", i);
|
||||
ret->vsh_constant_loc[i] = glGetUniformLocation(program, tmp);
|
||||
}
|
||||
ret->surface_size_loc = glGetUniformLocation(program, "surfaceSize");
|
||||
ret->clip_range_loc = glGetUniformLocation(program, "clipRange");
|
||||
ret->fog_color_loc = glGetUniformLocation(program, "fogColor");
|
||||
ret->fog_param_loc[0] = glGetUniformLocation(program, "fogParam[0]");
|
||||
ret->fog_param_loc[1] = glGetUniformLocation(program, "fogParam[1]");
|
||||
|
||||
ret->inv_viewport_loc = glGetUniformLocation(program, "invViewport");
|
||||
for (i = 0; i < NV2A_LTCTXA_COUNT; i++) {
|
||||
snprintf(tmp, sizeof(tmp), "ltctxa[%d]", i);
|
||||
ret->ltctxa_loc[i] = glGetUniformLocation(program, tmp);
|
||||
}
|
||||
for (i = 0; i < NV2A_LTCTXB_COUNT; i++) {
|
||||
snprintf(tmp, sizeof(tmp), "ltctxb[%d]", i);
|
||||
ret->ltctxb_loc[i] = glGetUniformLocation(program, tmp);
|
||||
}
|
||||
for (i = 0; i < NV2A_LTC1_COUNT; i++) {
|
||||
snprintf(tmp, sizeof(tmp), "ltc1[%d]", i);
|
||||
ret->ltc1_loc[i] = glGetUniformLocation(program, tmp);
|
||||
}
|
||||
for (i = 0; i < NV2A_MAX_LIGHTS; i++) {
|
||||
snprintf(tmp, sizeof(tmp), "lightInfiniteHalfVector%d", i);
|
||||
ret->light_infinite_half_vector_loc[i] = glGetUniformLocation(program, tmp);
|
||||
snprintf(tmp, sizeof(tmp), "lightInfiniteDirection%d", i);
|
||||
ret->light_infinite_direction_loc[i] = glGetUniformLocation(program, tmp);
|
||||
|
||||
snprintf(tmp, sizeof(tmp), "lightLocalPosition%d", i);
|
||||
ret->light_local_position_loc[i] = glGetUniformLocation(program, tmp);
|
||||
snprintf(tmp, sizeof(tmp), "lightLocalAttenuation%d", i);
|
||||
ret->light_local_attenuation_loc[i] = glGetUniformLocation(program, tmp);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* QEMU Geforce NV2A shader generator
|
||||
*
|
||||
* Copyright (c) 2015 espes
|
||||
* Copyright (c) 2015 Jannik Vogel
|
||||
*
|
||||
* 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 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef HW_NV2A_SHADERS_H
|
||||
#define HW_NV2A_SHADERS_H
|
||||
|
||||
//#include <SDL.h>
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include "nv2a_vsh.h"
|
||||
#include "nv2a_psh.h"
|
||||
#include "nv2a_int.h"
|
||||
|
||||
|
||||
enum ShaderPrimitiveMode {
|
||||
PRIM_TYPE_NONE,
|
||||
PRIM_TYPE_POINTS,
|
||||
PRIM_TYPE_LINES,
|
||||
PRIM_TYPE_LINE_LOOP,
|
||||
PRIM_TYPE_LINE_STRIP,
|
||||
PRIM_TYPE_TRIANGLES,
|
||||
PRIM_TYPE_TRIANGLE_STRIP,
|
||||
PRIM_TYPE_TRIANGLE_FAN,
|
||||
PRIM_TYPE_QUADS,
|
||||
PRIM_TYPE_QUAD_STRIP,
|
||||
PRIM_TYPE_POLYGON,
|
||||
};
|
||||
|
||||
enum ShaderPolygonMode {
|
||||
POLY_MODE_FILL,
|
||||
POLY_MODE_POINT,
|
||||
POLY_MODE_LINE,
|
||||
};
|
||||
|
||||
typedef struct ShaderState {
|
||||
PshState psh;
|
||||
|
||||
bool texture_matrix_enable[4];
|
||||
enum VshTexgen texgen[4][4];
|
||||
|
||||
bool fog_enable;
|
||||
enum VshFoggen foggen;
|
||||
enum VshFogMode fog_mode;
|
||||
|
||||
enum VshSkinning skinning;
|
||||
|
||||
bool normalization;
|
||||
|
||||
bool lighting;
|
||||
enum VshLight light[NV2A_MAX_LIGHTS];
|
||||
|
||||
bool fixed_function;
|
||||
|
||||
/* vertex program */
|
||||
bool vertex_program;
|
||||
uint32_t program_data[NV2A_MAX_TRANSFORM_PROGRAM_LENGTH][VSH_TOKEN_SIZE];
|
||||
int program_length;
|
||||
bool z_perspective;
|
||||
|
||||
/* primitive format for geometry shader */
|
||||
enum ShaderPolygonMode polygon_front_mode;
|
||||
enum ShaderPolygonMode polygon_back_mode;
|
||||
enum ShaderPrimitiveMode primitive_mode;
|
||||
} ShaderState;
|
||||
|
||||
typedef struct ShaderBinding {
|
||||
GLuint gl_program;
|
||||
GLenum gl_primitive_mode;
|
||||
|
||||
GLint psh_constant_loc[9][2];
|
||||
GLint alpha_ref_loc;
|
||||
|
||||
GLint bump_mat_loc[NV2A_MAX_TEXTURES];
|
||||
GLint bump_scale_loc[NV2A_MAX_TEXTURES];
|
||||
GLint bump_offset_loc[NV2A_MAX_TEXTURES];
|
||||
|
||||
GLint surface_size_loc;
|
||||
GLint clip_range_loc;
|
||||
|
||||
GLint vsh_constant_loc[NV2A_VERTEXSHADER_CONSTANTS];
|
||||
|
||||
GLint inv_viewport_loc;
|
||||
GLint ltctxa_loc[NV2A_LTCTXA_COUNT];
|
||||
GLint ltctxb_loc[NV2A_LTCTXB_COUNT];
|
||||
GLint ltc1_loc[NV2A_LTC1_COUNT];
|
||||
|
||||
GLint fog_color_loc;
|
||||
GLint fog_param_loc[2];
|
||||
GLint light_infinite_half_vector_loc[NV2A_MAX_LIGHTS];
|
||||
GLint light_infinite_direction_loc[NV2A_MAX_LIGHTS];
|
||||
GLint light_local_position_loc[NV2A_MAX_LIGHTS];
|
||||
GLint light_local_attenuation_loc[NV2A_MAX_LIGHTS];
|
||||
|
||||
} ShaderBinding;
|
||||
|
||||
ShaderBinding* generate_shaders(const ShaderState state);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* QEMU Geforce NV2A shader common definitions
|
||||
*
|
||||
* Copyright (c) 2015 espes
|
||||
* Copyright (c) 2015 Jannik Vogel
|
||||
*
|
||||
* 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 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef HW_NV2A_SHADERS_COMMON_H
|
||||
#define HW_NV2A_SHADERS_COMMON_H
|
||||
|
||||
#define STRUCT_VERTEX_DATA "struct VertexData {\n" \
|
||||
" float inv_w;\n" \
|
||||
" vec4 D0;\n" \
|
||||
" vec4 D1;\n" \
|
||||
" vec4 B0;\n" \
|
||||
" vec4 B1;\n" \
|
||||
" float Fog;\n" \
|
||||
" vec4 T0;\n" \
|
||||
" vec4 T1;\n" \
|
||||
" vec4 T2;\n" \
|
||||
" vec4 T3;\n" \
|
||||
"};\n"
|
||||
|
||||
#endif
|
|
@ -0,0 +1,772 @@
|
|||
/*
|
||||
* QEMU Geforce NV2A vertex shader translation
|
||||
*
|
||||
* Copyright (c) 2014 Jannik Vogel
|
||||
* Copyright (c) 2012 espes
|
||||
*
|
||||
* Based on:
|
||||
* Cxbx, VertexShader.cpp
|
||||
* Copyright (c) 2004 Aaron Robinson <caustik@caustik.com>
|
||||
* Kingofc <kingofc@freenet.de>
|
||||
* Dxbx, uPushBuffer.pas
|
||||
* Copyright (c) 2007 Shadow_tj, PatrickvL
|
||||
*
|
||||
* 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 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include "nv2a_shaders_common.h"
|
||||
#include "nv2a_vsh.h"
|
||||
|
||||
#define VSH_D3DSCM_CORRECTION 96
|
||||
|
||||
typedef enum {
|
||||
PARAM_UNKNOWN = 0,
|
||||
PARAM_R,
|
||||
PARAM_V,
|
||||
PARAM_C
|
||||
} VshParameterType;
|
||||
|
||||
typedef enum {
|
||||
OUTPUT_C = 0,
|
||||
OUTPUT_O
|
||||
} VshOutputType;
|
||||
|
||||
typedef enum {
|
||||
OMUX_MAC = 0,
|
||||
OMUX_ILU
|
||||
} VshOutputMux;
|
||||
|
||||
typedef enum {
|
||||
ILU_NOP = 0,
|
||||
ILU_MOV,
|
||||
ILU_RCP,
|
||||
ILU_RCC,
|
||||
ILU_RSQ,
|
||||
ILU_EXP,
|
||||
ILU_LOG,
|
||||
ILU_LIT
|
||||
} VshILU;
|
||||
|
||||
typedef enum {
|
||||
MAC_NOP,
|
||||
MAC_MOV,
|
||||
MAC_MUL,
|
||||
MAC_ADD,
|
||||
MAC_MAD,
|
||||
MAC_DP3,
|
||||
MAC_DPH,
|
||||
MAC_DP4,
|
||||
MAC_DST,
|
||||
MAC_MIN,
|
||||
MAC_MAX,
|
||||
MAC_SLT,
|
||||
MAC_SGE,
|
||||
MAC_ARL
|
||||
} VshMAC;
|
||||
|
||||
typedef enum {
|
||||
SWIZZLE_X = 0,
|
||||
SWIZZLE_Y,
|
||||
SWIZZLE_Z,
|
||||
SWIZZLE_W
|
||||
} VshSwizzle;
|
||||
|
||||
|
||||
typedef struct VshFieldMapping {
|
||||
VshFieldName field_name;
|
||||
uint8_t subtoken;
|
||||
uint8_t start_bit;
|
||||
uint8_t bit_length;
|
||||
} VshFieldMapping;
|
||||
|
||||
static const VshFieldMapping field_mapping[] = {
|
||||
// Field Name DWORD BitPos BitSize
|
||||
{ FLD_ILU, 1, 25, 3 },
|
||||
{ FLD_MAC, 1, 21, 4 },
|
||||
{ FLD_CONST, 1, 13, 8 },
|
||||
{ FLD_V, 1, 9, 4 },
|
||||
// INPUT A
|
||||
{ FLD_A_NEG, 1, 8, 1 },
|
||||
{ FLD_A_SWZ_X, 1, 6, 2 },
|
||||
{ FLD_A_SWZ_Y, 1, 4, 2 },
|
||||
{ FLD_A_SWZ_Z, 1, 2, 2 },
|
||||
{ FLD_A_SWZ_W, 1, 0, 2 },
|
||||
{ FLD_A_R, 2, 28, 4 },
|
||||
{ FLD_A_MUX, 2, 26, 2 },
|
||||
// INPUT B
|
||||
{ FLD_B_NEG, 2, 25, 1 },
|
||||
{ FLD_B_SWZ_X, 2, 23, 2 },
|
||||
{ FLD_B_SWZ_Y, 2, 21, 2 },
|
||||
{ FLD_B_SWZ_Z, 2, 19, 2 },
|
||||
{ FLD_B_SWZ_W, 2, 17, 2 },
|
||||
{ FLD_B_R, 2, 13, 4 },
|
||||
{ FLD_B_MUX, 2, 11, 2 },
|
||||
// INPUT C
|
||||
{ FLD_C_NEG, 2, 10, 1 },
|
||||
{ FLD_C_SWZ_X, 2, 8, 2 },
|
||||
{ FLD_C_SWZ_Y, 2, 6, 2 },
|
||||
{ FLD_C_SWZ_Z, 2, 4, 2 },
|
||||
{ FLD_C_SWZ_W, 2, 2, 2 },
|
||||
{ FLD_C_R_HIGH, 2, 0, 2 },
|
||||
{ FLD_C_R_LOW, 3, 30, 2 },
|
||||
{ FLD_C_MUX, 3, 28, 2 },
|
||||
// Output
|
||||
{ FLD_OUT_MAC_MASK, 3, 24, 4 },
|
||||
{ FLD_OUT_R, 3, 20, 4 },
|
||||
{ FLD_OUT_ILU_MASK, 3, 16, 4 },
|
||||
{ FLD_OUT_O_MASK, 3, 12, 4 },
|
||||
{ FLD_OUT_ORB, 3, 11, 1 },
|
||||
{ FLD_OUT_ADDRESS, 3, 3, 8 },
|
||||
{ FLD_OUT_MUX, 3, 2, 1 },
|
||||
// Other
|
||||
{ FLD_A0X, 3, 1, 1 },
|
||||
{ FLD_FINAL, 3, 0, 1 }
|
||||
};
|
||||
|
||||
|
||||
typedef struct VshOpcodeParams {
|
||||
bool A;
|
||||
bool B;
|
||||
bool C;
|
||||
} VshOpcodeParams;
|
||||
|
||||
#if 0
|
||||
static const VshOpcodeParams ilu_opcode_params[] = {
|
||||
/* ILU OP ParamA ParamB ParamC */
|
||||
/* ILU_NOP */ { false, false, false }, // Dxbx note : Unused
|
||||
/* ILU_MOV */ { false, false, true },
|
||||
/* ILU_RCP */ { false, false, true },
|
||||
/* ILU_RCC */ { false, false, true },
|
||||
/* ILU_RSQ */ { false, false, true },
|
||||
/* ILU_EXP */ { false, false, true },
|
||||
/* ILU_LOG */ { false, false, true },
|
||||
/* ILU_LIT */ { false, false, true },
|
||||
};
|
||||
#endif
|
||||
|
||||
static const VshOpcodeParams mac_opcode_params[] = {
|
||||
/* MAC OP ParamA ParamB ParamC */
|
||||
/* MAC_NOP */ { false, false, false }, // Dxbx note : Unused
|
||||
/* MAC_MOV */ { true, false, false },
|
||||
/* MAC_MUL */ { true, true, false },
|
||||
/* MAC_ADD */ { true, false, true },
|
||||
/* MAC_MAD */ { true, true, true },
|
||||
/* MAC_DP3 */ { true, true, false },
|
||||
/* MAC_DPH */ { true, true, false },
|
||||
/* MAC_DP4 */ { true, true, false },
|
||||
/* MAC_DST */ { true, true, false },
|
||||
/* MAC_MIN */ { true, true, false },
|
||||
/* MAC_MAX */ { true, true, false },
|
||||
/* MAC_SLT */ { true, true, false },
|
||||
/* MAC_SGE */ { true, true, false },
|
||||
/* MAC_ARL */ { true, false, false },
|
||||
};
|
||||
|
||||
|
||||
static const char* mask_str[] = {
|
||||
// xyzw xyzw
|
||||
",", // 0000 ____
|
||||
",w", // 0001 ___w
|
||||
",z", // 0010 __z_
|
||||
",zw", // 0011 __zw
|
||||
",y", // 0100 _y__
|
||||
",yw", // 0101 _y_w
|
||||
",yz", // 0110 _yz_
|
||||
",yzw", // 0111 _yzw
|
||||
",x", // 1000 x___
|
||||
",xw", // 1001 x__w
|
||||
",xz", // 1010 x_z_
|
||||
",xzw", // 1011 x_zw
|
||||
",xy", // 1100 xy__
|
||||
",xyw", // 1101 xy_w
|
||||
",xyz", // 1110 xyz_
|
||||
",xyzw" // 1111 xyzw
|
||||
};
|
||||
|
||||
/* Note: OpenGL seems to be case-sensitive, and requires upper-case opcodes! */
|
||||
static const char* mac_opcode[] = {
|
||||
"NOP",
|
||||
"MOV",
|
||||
"MUL",
|
||||
"ADD",
|
||||
"MAD",
|
||||
"DP3",
|
||||
"DPH",
|
||||
"DP4",
|
||||
"DST",
|
||||
"MIN",
|
||||
"MAX",
|
||||
"SLT",
|
||||
"SGE",
|
||||
"ARL A0.x", // Dxbx note : Alias for "mov a0.x"
|
||||
};
|
||||
|
||||
static const char* ilu_opcode[] = {
|
||||
"NOP",
|
||||
"MOV",
|
||||
"RCP",
|
||||
"RCC",
|
||||
"RSQ",
|
||||
"EXP",
|
||||
"LOG",
|
||||
"LIT",
|
||||
};
|
||||
|
||||
static bool ilu_force_scalar[] = {
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
};
|
||||
|
||||
static const char* out_reg_name[] = {
|
||||
"oPos",
|
||||
"???",
|
||||
"???",
|
||||
"oD0",
|
||||
"oD1",
|
||||
"oFog",
|
||||
"oPts",
|
||||
"oB0",
|
||||
"oB1",
|
||||
"oT0",
|
||||
"oT1",
|
||||
"oT2",
|
||||
"oT3",
|
||||
"???",
|
||||
"???",
|
||||
"A0.x",
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Retrieves a number of bits in the instruction token
|
||||
static int vsh_get_from_token(const uint32_t *shader_token,
|
||||
uint8_t subtoken,
|
||||
uint8_t start_bit,
|
||||
uint8_t bit_length)
|
||||
{
|
||||
return (shader_token[subtoken] >> start_bit) & ~(0xFFFFFFFF << bit_length);
|
||||
}
|
||||
|
||||
uint8_t vsh_get_field(const uint32_t *shader_token, VshFieldName field_name)
|
||||
{
|
||||
|
||||
return (uint8_t)(vsh_get_from_token(shader_token,
|
||||
field_mapping[field_name].subtoken,
|
||||
field_mapping[field_name].start_bit,
|
||||
field_mapping[field_name].bit_length));
|
||||
}
|
||||
|
||||
|
||||
// Converts the C register address to disassembly format
|
||||
static int16_t convert_c_register(const int16_t c_reg)
|
||||
{
|
||||
int16_t r = ((((c_reg >> 5) & 7) - 3) * 32) + (c_reg & 31);
|
||||
r += VSH_D3DSCM_CORRECTION; /* to map -96..95 to 0..191 */
|
||||
return r; //FIXME: = c_reg?!
|
||||
}
|
||||
|
||||
static std::string* decode_swizzle(
|
||||
const uint32_t *shader_token,
|
||||
VshFieldName swizzle_field
|
||||
)
|
||||
{
|
||||
const char* swizzle_str = "xyzw";
|
||||
uint8_t x, y, z, w;
|
||||
|
||||
/* some microcode instructions force a scalar value */
|
||||
if (swizzle_field == FLD_C_SWZ_X
|
||||
&& ilu_force_scalar[vsh_get_field(shader_token, FLD_ILU)]) {
|
||||
x = y = z = w = vsh_get_field(shader_token, swizzle_field);
|
||||
} else {
|
||||
int swizzle_field_i = (int)swizzle_field;
|
||||
x = vsh_get_field(shader_token, (VshFieldName) swizzle_field_i++);
|
||||
y = vsh_get_field(shader_token, (VshFieldName) swizzle_field_i++);
|
||||
z = vsh_get_field(shader_token, (VshFieldName) swizzle_field_i++);
|
||||
w = vsh_get_field(shader_token, (VshFieldName) swizzle_field_i);
|
||||
}
|
||||
|
||||
if (x == SWIZZLE_X && y == SWIZZLE_Y
|
||||
&& z == SWIZZLE_Z && w == SWIZZLE_W) {
|
||||
/* Don't print the swizzle if it's .xyzw */
|
||||
return new std::string(""); // Will turn ".xyzw" into "."
|
||||
/* Don't print duplicates */
|
||||
} else if (x == y && y == z && z == w) {
|
||||
return new std::string({'.', swizzle_str[x], '\0'});
|
||||
} else if (y == z && z == w) {
|
||||
return new std::string({'.',
|
||||
swizzle_str[x], swizzle_str[y], '\0'});
|
||||
} else if (z == w) {
|
||||
return new std::string({'.',
|
||||
swizzle_str[x], swizzle_str[y], swizzle_str[z], '\0'});
|
||||
} else {
|
||||
return new std::string({'.',
|
||||
swizzle_str[x], swizzle_str[y],
|
||||
swizzle_str[z], swizzle_str[w],
|
||||
'\0'}); // Normal swizzle mask
|
||||
}
|
||||
}
|
||||
|
||||
static std::string* decode_opcode_input(const uint32_t *shader_token,
|
||||
VshParameterType param,
|
||||
VshFieldName neg_field,
|
||||
int reg_num)
|
||||
{
|
||||
/* This function decodes a vertex shader opcode parameter into a string.
|
||||
* Input A, B or C is controlled via the Param and NEG fieldnames,
|
||||
* the R-register address for each input is already given by caller. */
|
||||
|
||||
std::string *ret_str = new std::string();
|
||||
|
||||
if (vsh_get_field(shader_token, neg_field) > 0) {
|
||||
ret_str->append("-");
|
||||
}
|
||||
|
||||
/* PARAM_R uses the supplied reg_num, but the other two need to be
|
||||
* determined */
|
||||
char tmp[40];
|
||||
switch (param) {
|
||||
case PARAM_R:
|
||||
snprintf(tmp, sizeof(tmp), "R%d", reg_num);
|
||||
break;
|
||||
case PARAM_V:
|
||||
reg_num = vsh_get_field(shader_token, FLD_V);
|
||||
snprintf(tmp, sizeof(tmp), "v%d", reg_num);
|
||||
break;
|
||||
case PARAM_C:
|
||||
reg_num = convert_c_register(vsh_get_field(shader_token, FLD_CONST));
|
||||
if (vsh_get_field(shader_token, FLD_A0X) > 0) {
|
||||
//FIXME: does this really require the "correction" doe in convert_c_register?!
|
||||
snprintf(tmp, sizeof(tmp), "c[A0+%d]", reg_num);
|
||||
} else {
|
||||
snprintf(tmp, sizeof(tmp), "c[%d]", reg_num);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown vs param: 0x%x\n", param);
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
ret_str->append(tmp);
|
||||
|
||||
{
|
||||
/* swizzle bits are next to the neg bit */
|
||||
std::string *swizzle_str = decode_swizzle(shader_token, (VshFieldName)(neg_field+1));
|
||||
ret_str->append(*swizzle_str);
|
||||
delete swizzle_str;
|
||||
}
|
||||
|
||||
return ret_str;
|
||||
}
|
||||
|
||||
|
||||
static std::string* decode_opcode(
|
||||
const uint32_t *shader_token,
|
||||
VshOutputMux out_mux,
|
||||
uint32_t mask,
|
||||
const char *opcode,
|
||||
const char *inputs
|
||||
)
|
||||
{
|
||||
char buf[128];
|
||||
std::string *ret = new std::string();
|
||||
int reg_num = vsh_get_field(shader_token, FLD_OUT_R);
|
||||
|
||||
/* Test for paired opcodes (in other words : Are both <> NOP?) */
|
||||
if (out_mux == OMUX_MAC
|
||||
&& vsh_get_field(shader_token, FLD_ILU) != ILU_NOP
|
||||
&& reg_num == 1) {
|
||||
/* Ignore paired MAC opcodes that write to R1 */
|
||||
mask = 0;
|
||||
} else if (out_mux == OMUX_ILU
|
||||
&& vsh_get_field(shader_token, FLD_MAC) != MAC_NOP) {
|
||||
/* Paired ILU opcodes can only write to R1 */
|
||||
reg_num = 1;
|
||||
}
|
||||
|
||||
if (strcmp(opcode, mac_opcode[MAC_ARL]) == 0) {
|
||||
snprintf(buf, sizeof(buf), " ARL(A0%s);\n", inputs);
|
||||
ret->append(buf);
|
||||
} else if (mask > 0) {
|
||||
snprintf(buf, sizeof(buf), " %s(R%d%s%s);\n",
|
||||
opcode, reg_num, mask_str[mask], inputs);
|
||||
ret->append(buf);
|
||||
}
|
||||
|
||||
/* See if we must add a muxed opcode too: */
|
||||
if (vsh_get_field(shader_token, FLD_OUT_MUX) == out_mux
|
||||
/* Only if it's not masked away: */
|
||||
&& vsh_get_field(shader_token, FLD_OUT_O_MASK) != 0) {
|
||||
|
||||
ret->append(" ");
|
||||
ret->append(opcode);
|
||||
ret->append("(");
|
||||
|
||||
if (vsh_get_field(shader_token, FLD_OUT_ORB) == OUTPUT_C) {
|
||||
/* TODO : Emulate writeable const registers */
|
||||
ret->append("c");
|
||||
snprintf(buf, sizeof(buf), "%d", convert_c_register(
|
||||
vsh_get_field(shader_token, FLD_OUT_ADDRESS)));
|
||||
ret->append(buf);
|
||||
} else {
|
||||
ret->append(out_reg_name[
|
||||
vsh_get_field(shader_token, FLD_OUT_ADDRESS) & 0xF]);
|
||||
}
|
||||
ret->append(mask_str[
|
||||
vsh_get_field(shader_token, FLD_OUT_O_MASK)]);
|
||||
ret->append(inputs);
|
||||
ret->append(");\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static std::string* decode_token(const uint32_t *shader_token)
|
||||
{
|
||||
std::string *ret;
|
||||
|
||||
/* Since it's potentially used twice, decode input C once: */
|
||||
std::string *input_c =
|
||||
decode_opcode_input(shader_token,
|
||||
(VshParameterType) vsh_get_field(shader_token, FLD_C_MUX),
|
||||
FLD_C_NEG,
|
||||
(vsh_get_field(shader_token, FLD_C_R_HIGH) << 2)
|
||||
| vsh_get_field(shader_token, FLD_C_R_LOW));
|
||||
|
||||
/* See what MAC opcode is written to (if not masked away): */
|
||||
VshMAC mac = (VshMAC) vsh_get_field(shader_token, FLD_MAC);
|
||||
if (mac != MAC_NOP) {
|
||||
std::string *inputs_mac = new std::string();
|
||||
if (mac_opcode_params[mac].A) {
|
||||
std::string *input_a =
|
||||
decode_opcode_input(shader_token,
|
||||
(VshParameterType) vsh_get_field(shader_token, FLD_A_MUX),
|
||||
FLD_A_NEG,
|
||||
vsh_get_field(shader_token, FLD_A_R));
|
||||
inputs_mac->append(", ");
|
||||
inputs_mac->append(*input_a);
|
||||
delete input_a;
|
||||
}
|
||||
if (mac_opcode_params[mac].B) {
|
||||
std::string *input_b =
|
||||
decode_opcode_input(shader_token,
|
||||
(VshParameterType) vsh_get_field(shader_token, FLD_B_MUX),
|
||||
FLD_B_NEG,
|
||||
vsh_get_field(shader_token, FLD_B_R));
|
||||
inputs_mac->append(", ");
|
||||
inputs_mac->append(*input_b);
|
||||
delete input_b;
|
||||
}
|
||||
if (mac_opcode_params[mac].C) {
|
||||
inputs_mac->append(", ");
|
||||
inputs_mac->append(*input_c);
|
||||
}
|
||||
|
||||
/* Then prepend these inputs with the actual opcode, mask, and input : */
|
||||
ret = decode_opcode(shader_token,
|
||||
OMUX_MAC,
|
||||
vsh_get_field(shader_token, FLD_OUT_MAC_MASK),
|
||||
mac_opcode[mac],
|
||||
inputs_mac->c_str());
|
||||
delete inputs_mac;
|
||||
} else {
|
||||
ret = new std::string();
|
||||
}
|
||||
|
||||
/* See if a ILU opcode is present too: */
|
||||
VshILU ilu = (VshILU) vsh_get_field(shader_token, FLD_ILU);
|
||||
if (ilu != ILU_NOP) {
|
||||
std::string *inputs_c = new std::string(", ");
|
||||
inputs_c->append(*input_c);
|
||||
|
||||
/* Append the ILU opcode, mask and (the already determined) input C: */
|
||||
std::string *ilu_op =
|
||||
decode_opcode(shader_token,
|
||||
OMUX_ILU,
|
||||
vsh_get_field(shader_token, FLD_OUT_ILU_MASK),
|
||||
ilu_opcode[ilu],
|
||||
inputs_c->c_str());
|
||||
|
||||
ret->append(*ilu_op);
|
||||
|
||||
delete inputs_c;
|
||||
delete ilu_op;
|
||||
}
|
||||
|
||||
delete input_c;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char* vsh_header =
|
||||
"\n"
|
||||
"int A0 = 0;\n"
|
||||
"\n"
|
||||
"vec4 R0 = vec4(0.0,0.0,0.0,0.0);\n"
|
||||
"vec4 R1 = vec4(0.0,0.0,0.0,0.0);\n"
|
||||
"vec4 R2 = vec4(0.0,0.0,0.0,0.0);\n"
|
||||
"vec4 R3 = vec4(0.0,0.0,0.0,0.0);\n"
|
||||
"vec4 R4 = vec4(0.0,0.0,0.0,0.0);\n"
|
||||
"vec4 R5 = vec4(0.0,0.0,0.0,0.0);\n"
|
||||
"vec4 R6 = vec4(0.0,0.0,0.0,0.0);\n"
|
||||
"vec4 R7 = vec4(0.0,0.0,0.0,0.0);\n"
|
||||
"vec4 R8 = vec4(0.0,0.0,0.0,0.0);\n"
|
||||
"vec4 R9 = vec4(0.0,0.0,0.0,0.0);\n"
|
||||
"vec4 R10 = vec4(0.0,0.0,0.0,0.0);\n"
|
||||
"vec4 R11 = vec4(0.0,0.0,0.0,0.0);\n"
|
||||
"#define R12 oPos\n" /* R12 is a mirror of oPos */
|
||||
"\n"
|
||||
|
||||
/* See:
|
||||
* http://msdn.microsoft.com/en-us/library/windows/desktop/bb174703%28v=vs.85%29.aspx
|
||||
* https://www.opengl.org/registry/specs/NV/vertex_program1_1.txt
|
||||
*/
|
||||
"\n"
|
||||
//QQQ #ifdef NICE_CODE
|
||||
"/* Converts the input to vec4, pads with last component */\n"
|
||||
"vec4 _in(float v) { return vec4(v); }\n"
|
||||
"vec4 _in(vec2 v) { return v.xyyy; }\n"
|
||||
"vec4 _in(vec3 v) { return v.xyzz; }\n"
|
||||
"vec4 _in(vec4 v) { return v.xyzw; }\n"
|
||||
//#else
|
||||
// "/* Make sure input is always a vec4 */\n"
|
||||
// "#define _in(v) vec4(v)\n"
|
||||
//#endif
|
||||
"\n"
|
||||
"#define INFINITY (1.0 / 0.0)\n"
|
||||
"\n"
|
||||
"#define MOV(dest, mask, src) dest.mask = _MOV(_in(src)).mask\n"
|
||||
"vec4 _MOV(vec4 src)\n"
|
||||
"{\n"
|
||||
" return src;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"#define MUL(dest, mask, src0, src1) dest.mask = _MUL(_in(src0), _in(src1)).mask\n"
|
||||
"vec4 _MUL(vec4 src0, vec4 src1)\n"
|
||||
"{\n"
|
||||
" return src0 * src1;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"#define ADD(dest, mask, src0, src1) dest.mask = _ADD(_in(src0), _in(src1)).mask\n"
|
||||
"vec4 _ADD(vec4 src0, vec4 src1)\n"
|
||||
"{\n"
|
||||
" return src0 + src1;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"#define MAD(dest, mask, src0, src1, src2) dest.mask = _MAD(_in(src0), _in(src1), _in(src2)).mask\n"
|
||||
"vec4 _MAD(vec4 src0, vec4 src1, vec4 src2)\n"
|
||||
"{\n"
|
||||
" return src0 * src1 + src2;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"#define DP3(dest, mask, src0, src1) dest.mask = _DP3(_in(src0), _in(src1)).mask\n"
|
||||
"vec4 _DP3(vec4 src0, vec4 src1)\n"
|
||||
"{\n"
|
||||
" return vec4(dot(src0.xyz, src1.xyz));\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"#define DPH(dest, mask, src0, src1) dest.mask = _DPH(_in(src0), _in(src1)).mask\n"
|
||||
"vec4 _DPH(vec4 src0, vec4 src1)\n"
|
||||
"{\n"
|
||||
" return vec4(dot(vec4(src0.xyz, 1.0), src1));\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"#define DP4(dest, mask, src0, src1) dest.mask = _DP4(_in(src0), _in(src1)).mask\n"
|
||||
"vec4 _DP4(vec4 src0, vec4 src1)\n"
|
||||
"{\n"
|
||||
" return vec4(dot(src0, src1));\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"#define DST(dest, mask, src0, src1) dest.mask = _DST(_in(src0), _in(src1)).mask\n"
|
||||
"vec4 _DST(vec4 src0, vec4 src1)\n"
|
||||
"{\n"
|
||||
" return vec4(1.0,\n"
|
||||
" src0.y * src1.y,\n"
|
||||
" src0.z,\n"
|
||||
" src1.w);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"#define MIN(dest, mask, src0, src1) dest.mask = _MIN(_in(src0), _in(src1)).mask\n"
|
||||
"vec4 _MIN(vec4 src0, vec4 src1)\n"
|
||||
"{\n"
|
||||
" return min(src0, src1);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"#define MAX(dest, mask, src0, src1) dest.mask = _MAX(_in(src0), _in(src1)).mask\n"
|
||||
"vec4 _MAX(vec4 src0, vec4 src1)\n"
|
||||
"{\n"
|
||||
" return max(src0, src1);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"#define SLT(dest, mask, src0, src1) dest.mask = _SLT(_in(src0), _in(src1)).mask\n"
|
||||
"vec4 _SLT(vec4 src0, vec4 src1)\n"
|
||||
"{\n"
|
||||
" return vec4(lessThan(src0, src1));\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"#define ARL(dest, src) dest = _ARL(_in(src).x)\n"
|
||||
"int _ARL(float src)\n"
|
||||
"{\n"
|
||||
" return int(floor(src));\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"#define SGE(dest, mask, src0, src1) dest.mask = _SGE(_in(src0), _in(src1)).mask\n"
|
||||
"vec4 _SGE(vec4 src0, vec4 src1)\n"
|
||||
"{\n"
|
||||
" return vec4(greaterThanEqual(src0, src1));\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"#define RCP(dest, mask, src) dest.mask = _RCP(_in(src).x).mask\n"
|
||||
"vec4 _RCP(float src)\n"
|
||||
"{\n"
|
||||
" return vec4(1.0 / src);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"#define RCC(dest, mask, src) dest.mask = _RCC(_in(src).x).mask\n"
|
||||
"vec4 _RCC(float src)\n"
|
||||
"{\n"
|
||||
" float t = 1.0 / src;\n"
|
||||
" if (t > 0.0) {\n"
|
||||
" t = clamp(t, 5.42101e-020, 1.884467e+019);\n"
|
||||
" } else {\n"
|
||||
" t = clamp(t, -1.884467e+019, -5.42101e-020);\n"
|
||||
" }\n"
|
||||
" return vec4(t);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"#define RSQ(dest, mask, src) dest.mask = _RSQ(_in(src).x).mask\n"
|
||||
"vec4 _RSQ(float src)\n"
|
||||
"{\n"
|
||||
" if (src == 0.0) { return vec4(INFINITY); }\n"
|
||||
" if (isinf(src)) { return vec4(0.0); }\n"
|
||||
" return vec4(inversesqrt(abs(src)));\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"#define EXP(dest, mask, src) dest.mask = _EXP(_in(src).x).mask\n"
|
||||
"vec4 _EXP(float src)\n"
|
||||
"{\n"
|
||||
" return vec4(exp2(src));\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"#define LOG(dest, mask, src) dest.mask = _LOG(_in(src).x).mask\n"
|
||||
"vec4 _LOG(float src)\n"
|
||||
"{\n"
|
||||
" return vec4(log2(src));\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"#define LIT(dest, mask, src) dest.mask = _LIT(_in(src)).mask\n"
|
||||
"vec4 _LIT(vec4 src)\n"
|
||||
"{\n"
|
||||
" vec4 s = src;\n"
|
||||
" float epsilon = 1.0 / 256.0;\n"
|
||||
" s.w = clamp(s.w, -(128.0 - epsilon), 128.0 - epsilon);\n"
|
||||
" s.x = max(s.x, 0.0);\n"
|
||||
" s.y = max(s.y, 0.0);\n"
|
||||
" vec4 t = vec4(1.0, 0.0, 0.0, 1.0);\n"
|
||||
" t.y = s.x;\n"
|
||||
#if 1
|
||||
" t.z = (s.x > 0.0) ? exp2(s.w * log2(s.y)) : 0.0;\n"
|
||||
#else
|
||||
" t.z = (s.x > 0.0) ? pow(s.y, s.w) : 0.0;\n"
|
||||
#endif
|
||||
" return t;\n"
|
||||
"}\n";
|
||||
|
||||
void vsh_translate(uint16_t version,
|
||||
const uint32_t *tokens,
|
||||
unsigned int length,
|
||||
bool z_perspective,
|
||||
std::string *header, std::string *body)
|
||||
{
|
||||
char buf[128];
|
||||
header->append(vsh_header);
|
||||
|
||||
bool has_final = false;
|
||||
unsigned int slot;
|
||||
for (slot=0; slot < length; slot++) {
|
||||
const uint32_t* cur_token = &tokens[slot * VSH_TOKEN_SIZE];
|
||||
std::string *token_str = decode_token(cur_token);
|
||||
snprintf(buf, sizeof(buf),
|
||||
" /* Slot %d: 0x%08X 0x%08X 0x%08X 0x%08X */",
|
||||
slot, cur_token[0],cur_token[1],cur_token[2],cur_token[3]);
|
||||
body->append(buf);
|
||||
body->append("\n");
|
||||
body->append(*token_str);
|
||||
body->append("\n");
|
||||
delete token_str;
|
||||
|
||||
if (vsh_get_field(cur_token, FLD_FINAL)) {
|
||||
has_final = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(has_final);
|
||||
|
||||
/* pre-divide and output the generated W so we can do persepctive correct
|
||||
* interpolation manually. OpenGL can't, since we give it a W of 1 to work
|
||||
* around the perspective divide */
|
||||
body->append(
|
||||
" if (oPos.w == 0.0 || isinf(oPos.w)) {\n"
|
||||
" vtx.inv_w = 1.0;\n"
|
||||
" } else {\n"
|
||||
" vtx.inv_w = 1.0 / oPos.w;\n"
|
||||
" }\n"
|
||||
);
|
||||
|
||||
body->append(
|
||||
/* the shaders leave the result in screen space, while
|
||||
* opengl expects it in clip space.
|
||||
* TODO: the pixel-center co-ordinate differences should handled
|
||||
*/
|
||||
" oPos.x = 2.0 * (oPos.x - surfaceSize.x * 0.5) / surfaceSize.x;\n"
|
||||
" oPos.y = -2.0 * (oPos.y - surfaceSize.y * 0.5) / surfaceSize.y;\n"
|
||||
);
|
||||
if (z_perspective) {
|
||||
body->append(" oPos.z = oPos.w;\n");
|
||||
}
|
||||
body->append(
|
||||
/* Map the clip range into clip space so z is clipped correctly.
|
||||
* Note this makes the values in the depth buffer wrong. This should be
|
||||
* handled with gl_ClipDistance instead, but that has performance issues
|
||||
* on OS X.
|
||||
*/
|
||||
" if (clipRange.y != clipRange.x) {\n"
|
||||
" oPos.z = (oPos.z - 0.5 * (clipRange.x + clipRange.y)) / (0.5 * (clipRange.y - clipRange.x));\n"
|
||||
" }\n"
|
||||
|
||||
/* Correct for the perspective divide */
|
||||
" if (oPos.w < 0.0) {\n"
|
||||
/* undo the perspective divide in the case where the point would be
|
||||
* clipped so opengl can clip it correctly */
|
||||
" oPos.xyz *= oPos.w;\n"
|
||||
" } else {\n"
|
||||
/* we don't want the OpenGL perspective divide to happen, but we
|
||||
* can't multiply by W because it could be meaningless here */
|
||||
" oPos.w = 1.0;\n"
|
||||
" }\n"
|
||||
);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* QEMU Geforce NV2A vertex shader translation
|
||||
*
|
||||
* Copyright (c) 2012 espes
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
* Contributions after 2012-01-13 are licensed under the terms of the
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef HW_NV2A_VSH_H
|
||||
#define HW_NV2A_VSH_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
enum VshLight {
|
||||
LIGHT_OFF,
|
||||
LIGHT_INFINITE,
|
||||
LIGHT_LOCAL,
|
||||
LIGHT_SPOT
|
||||
};
|
||||
|
||||
enum VshTexgen {
|
||||
TEXGEN_DISABLE,
|
||||
TEXGEN_EYE_LINEAR,
|
||||
TEXGEN_OBJECT_LINEAR,
|
||||
TEXGEN_SPHERE_MAP,
|
||||
TEXGEN_NORMAL_MAP,
|
||||
TEXGEN_REFLECTION_MAP,
|
||||
};
|
||||
|
||||
enum VshFogMode {
|
||||
FOG_MODE_LINEAR,
|
||||
FOG_MODE_EXP,
|
||||
FOG_MODE_ERROR2, /* Doesn't exist */
|
||||
FOG_MODE_EXP2,
|
||||
FOG_MODE_LINEAR_ABS,
|
||||
FOG_MODE_EXP_ABS,
|
||||
FOG_MODE_ERROR6, /* Doesn't exist */
|
||||
FOG_MODE_EXP2_ABS
|
||||
};
|
||||
|
||||
enum VshFoggen {
|
||||
FOGGEN_SPEC_ALPHA,
|
||||
FOGGEN_RADIAL,
|
||||
FOGGEN_PLANAR,
|
||||
FOGGEN_ABS_PLANAR,
|
||||
FOGGEN_ERROR4,
|
||||
FOGGEN_ERROR5,
|
||||
FOGGEN_FOG_X
|
||||
};
|
||||
|
||||
enum VshSkinning {
|
||||
SKINNING_OFF,
|
||||
SKINNING_1WEIGHTS,
|
||||
SKINNING_2WEIGHTS,
|
||||
SKINNING_3WEIGHTS,
|
||||
SKINNING_2WEIGHTS2MATRICES,
|
||||
SKINNING_3WEIGHTS3MATRICES,
|
||||
SKINNING_4WEIGHTS4MATRICES,
|
||||
};
|
||||
|
||||
// vs.1.1, not an official value
|
||||
#define VSH_VERSION_VS 0xF078
|
||||
|
||||
// Xbox vertex shader
|
||||
#define VSH_VERSION_XVS 0x2078
|
||||
|
||||
// Xbox vertex state shader
|
||||
#define VSH_VERSION_XVSS 0x7378
|
||||
|
||||
// Xbox vertex read/write shader
|
||||
#define VSH_VERSION_XVSW 0x7778
|
||||
|
||||
#define VSH_TOKEN_SIZE 4
|
||||
|
||||
typedef enum {
|
||||
FLD_ILU = 0,
|
||||
FLD_MAC,
|
||||
FLD_CONST,
|
||||
FLD_V,
|
||||
// Input A
|
||||
FLD_A_NEG,
|
||||
FLD_A_SWZ_X,
|
||||
FLD_A_SWZ_Y,
|
||||
FLD_A_SWZ_Z,
|
||||
FLD_A_SWZ_W,
|
||||
FLD_A_R,
|
||||
FLD_A_MUX,
|
||||
// Input B
|
||||
FLD_B_NEG,
|
||||
FLD_B_SWZ_X,
|
||||
FLD_B_SWZ_Y,
|
||||
FLD_B_SWZ_Z,
|
||||
FLD_B_SWZ_W,
|
||||
FLD_B_R,
|
||||
FLD_B_MUX,
|
||||
// Input C
|
||||
FLD_C_NEG,
|
||||
FLD_C_SWZ_X,
|
||||
FLD_C_SWZ_Y,
|
||||
FLD_C_SWZ_Z,
|
||||
FLD_C_SWZ_W,
|
||||
FLD_C_R_HIGH,
|
||||
FLD_C_R_LOW,
|
||||
FLD_C_MUX,
|
||||
// Output
|
||||
FLD_OUT_MAC_MASK,
|
||||
FLD_OUT_R,
|
||||
FLD_OUT_ILU_MASK,
|
||||
FLD_OUT_O_MASK,
|
||||
FLD_OUT_ORB,
|
||||
FLD_OUT_ADDRESS,
|
||||
FLD_OUT_MUX,
|
||||
// Relative addressing
|
||||
FLD_A0X,
|
||||
// Final instruction
|
||||
FLD_FINAL
|
||||
} VshFieldName;
|
||||
|
||||
uint8_t vsh_get_field(const uint32_t *shader_token, VshFieldName field_name);
|
||||
|
||||
void vsh_translate(uint16_t version,
|
||||
const uint32_t *tokens,
|
||||
unsigned int length,
|
||||
bool z_perspective,
|
||||
std::string *header, std::string *body);
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,414 @@
|
|||
/* $NetBSD: queue.h,v 1.52 2009/04/20 09:56:08 mschuett Exp $ */
|
||||
|
||||
/*
|
||||
* QEMU version: Copy from netbsd, removed debug code, removed some of
|
||||
* the implementations. Left in singly-linked lists, lists, simple
|
||||
* queues, and tail queues.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||
*/
|
||||
|
||||
#ifndef QEMU_SYS_QUEUE_H_
|
||||
#define QEMU_SYS_QUEUE_H_
|
||||
|
||||
/*
|
||||
* This file defines four types of data structures: singly-linked lists,
|
||||
* lists, simple queues, and tail queues.
|
||||
*
|
||||
* A singly-linked list is headed by a single forward pointer. The
|
||||
* elements are singly linked for minimum space and pointer manipulation
|
||||
* overhead at the expense of O(n) removal for arbitrary elements. New
|
||||
* elements can be added to the list after an existing element or at the
|
||||
* head of the list. Elements being removed from the head of the list
|
||||
* should use the explicit macro for this purpose for optimum
|
||||
* efficiency. A singly-linked list may only be traversed in the forward
|
||||
* direction. Singly-linked lists are ideal for applications with large
|
||||
* datasets and few or no removals or for implementing a LIFO queue.
|
||||
*
|
||||
* A list is headed by a single forward pointer (or an array of forward
|
||||
* pointers for a hash table header). The elements are doubly linked
|
||||
* so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before
|
||||
* or after an existing element or at the head of the list. A list
|
||||
* may only be traversed in the forward direction.
|
||||
*
|
||||
* A simple queue is headed by a pair of pointers, one the head of the
|
||||
* list and the other to the tail of the list. The elements are singly
|
||||
* linked to save space, so elements can only be removed from the
|
||||
* head of the list. New elements can be added to the list after
|
||||
* an existing element, at the head of the list, or at the end of the
|
||||
* list. A simple queue may only be traversed in the forward direction.
|
||||
*
|
||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or
|
||||
* after an existing element, at the head of the list, or at the end of
|
||||
* the list. A tail queue may be traversed in either direction.
|
||||
*
|
||||
* For details on the use of these macros, see the queue(3) manual page.
|
||||
*/
|
||||
|
||||
// #include "qemu/atomic.h" /* for smp_wmb() */
|
||||
|
||||
/*
|
||||
* List definitions.
|
||||
*/
|
||||
#define QLIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *lh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define QLIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define QLIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *le_next; /* next element */ \
|
||||
struct type **le_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* List functions.
|
||||
*/
|
||||
#define QLIST_INIT(head) do { \
|
||||
(head)->lh_first = NULL; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QLIST_INSERT_AFTER(listelm, elm, field) do { \
|
||||
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
|
||||
(listelm)->field.le_next->field.le_prev = \
|
||||
&(elm)->field.le_next; \
|
||||
(listelm)->field.le_next = (elm); \
|
||||
(elm)->field.le_prev = &(listelm)->field.le_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QLIST_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
||||
(elm)->field.le_next = (listelm); \
|
||||
*(listelm)->field.le_prev = (elm); \
|
||||
(listelm)->field.le_prev = &(elm)->field.le_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QLIST_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
|
||||
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
|
||||
(head)->lh_first = (elm); \
|
||||
(elm)->field.le_prev = &(head)->lh_first; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QLIST_INSERT_HEAD_RCU(head, elm, field) do { \
|
||||
(elm)->field.le_prev = &(head)->lh_first; \
|
||||
(elm)->field.le_next = (head)->lh_first; \
|
||||
smp_wmb(); /* fill elm before linking it */ \
|
||||
if ((head)->lh_first != NULL) { \
|
||||
(head)->lh_first->field.le_prev = &(elm)->field.le_next; \
|
||||
} \
|
||||
(head)->lh_first = (elm); \
|
||||
smp_wmb(); \
|
||||
} while (/* CONSTCOND*/0)
|
||||
|
||||
#define QLIST_REMOVE(elm, field) do { \
|
||||
if ((elm)->field.le_next != NULL) \
|
||||
(elm)->field.le_next->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = (elm)->field.le_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QLIST_FOREACH(var, head, field) \
|
||||
for ((var) = ((head)->lh_first); \
|
||||
(var); \
|
||||
(var) = ((var)->field.le_next))
|
||||
|
||||
#define QLIST_FOREACH_SAFE(var, head, field, next_var) \
|
||||
for ((var) = ((head)->lh_first); \
|
||||
(var) && ((next_var) = ((var)->field.le_next), 1); \
|
||||
(var) = (next_var))
|
||||
|
||||
/*
|
||||
* List access methods.
|
||||
*/
|
||||
#define QLIST_EMPTY(head) ((head)->lh_first == NULL)
|
||||
#define QLIST_FIRST(head) ((head)->lh_first)
|
||||
#define QLIST_NEXT(elm, field) ((elm)->field.le_next)
|
||||
|
||||
|
||||
/*
|
||||
* Singly-linked List definitions.
|
||||
*/
|
||||
#define QSLIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *slh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define QSLIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define QSLIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sle_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked List functions.
|
||||
*/
|
||||
#define QSLIST_INIT(head) do { \
|
||||
(head)->slh_first = NULL; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QSLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
||||
(elm)->field.sle_next = (slistelm)->field.sle_next; \
|
||||
(slistelm)->field.sle_next = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QSLIST_INSERT_HEAD(head, elm, field) do { \
|
||||
(elm)->field.sle_next = (head)->slh_first; \
|
||||
(head)->slh_first = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QSLIST_REMOVE_HEAD(head, field) do { \
|
||||
(head)->slh_first = (head)->slh_first->field.sle_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QSLIST_REMOVE_AFTER(slistelm, field) do { \
|
||||
(slistelm)->field.sle_next = \
|
||||
QSLIST_NEXT(QSLIST_NEXT((slistelm), field), field); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QSLIST_FOREACH(var, head, field) \
|
||||
for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next)
|
||||
|
||||
#define QSLIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = QSLIST_FIRST((head)); \
|
||||
(var) && ((tvar) = QSLIST_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* Singly-linked List access methods.
|
||||
*/
|
||||
#define QSLIST_EMPTY(head) ((head)->slh_first == NULL)
|
||||
#define QSLIST_FIRST(head) ((head)->slh_first)
|
||||
#define QSLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
||||
|
||||
|
||||
/*
|
||||
* Simple queue definitions.
|
||||
*/
|
||||
#define QSIMPLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sqh_first; /* first element */ \
|
||||
struct type **sqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define QSIMPLEQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).sqh_first }
|
||||
|
||||
#define QSIMPLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple queue functions.
|
||||
*/
|
||||
#define QSIMPLEQ_INIT(head) do { \
|
||||
(head)->sqh_first = NULL; \
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QSIMPLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(head)->sqh_first = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QSIMPLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.sqe_next = NULL; \
|
||||
*(head)->sqh_last = (elm); \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL) \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(listelm)->field.sqe_next = (elm); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QSIMPLEQ_REMOVE_HEAD(head, field) do { \
|
||||
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL)\
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QSIMPLEQ_REMOVE(head, elm, type, field) do { \
|
||||
if ((head)->sqh_first == (elm)) { \
|
||||
QSIMPLEQ_REMOVE_HEAD((head), field); \
|
||||
} else { \
|
||||
struct type *curelm = (head)->sqh_first; \
|
||||
while (curelm->field.sqe_next != (elm)) \
|
||||
curelm = curelm->field.sqe_next; \
|
||||
if ((curelm->field.sqe_next = \
|
||||
curelm->field.sqe_next->field.sqe_next) == NULL) \
|
||||
(head)->sqh_last = &(curelm)->field.sqe_next; \
|
||||
} \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QSIMPLEQ_FOREACH(var, head, field) \
|
||||
for ((var) = ((head)->sqh_first); \
|
||||
(var); \
|
||||
(var) = ((var)->field.sqe_next))
|
||||
|
||||
#define QSIMPLEQ_FOREACH_SAFE(var, head, field, next) \
|
||||
for ((var) = ((head)->sqh_first); \
|
||||
(var) && ((next = ((var)->field.sqe_next)), 1); \
|
||||
(var) = (next))
|
||||
|
||||
#define QSIMPLEQ_CONCAT(head1, head2) do { \
|
||||
if (!QSIMPLEQ_EMPTY((head2))) { \
|
||||
*(head1)->sqh_last = (head2)->sqh_first; \
|
||||
(head1)->sqh_last = (head2)->sqh_last; \
|
||||
QSIMPLEQ_INIT((head2)); \
|
||||
} \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QSIMPLEQ_LAST(head, type, field) \
|
||||
(QSIMPLEQ_EMPTY((head)) ? \
|
||||
NULL : \
|
||||
((struct type *)(void *) \
|
||||
((char *)((head)->sqh_last) - offsetof(struct type, field))))
|
||||
|
||||
/*
|
||||
* Simple queue access methods.
|
||||
*/
|
||||
#define QSIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL)
|
||||
#define QSIMPLEQ_FIRST(head) ((head)->sqh_first)
|
||||
#define QSIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
|
||||
|
||||
|
||||
/*
|
||||
* Tail queue definitions.
|
||||
*/
|
||||
#define Q_TAILQ_HEAD(name, type, qual) \
|
||||
struct name { \
|
||||
qual type *tqh_first; /* first element */ \
|
||||
qual type *qual *tqh_last; /* addr of last next element */ \
|
||||
}
|
||||
#define QTAILQ_HEAD(name, type) Q_TAILQ_HEAD(name, struct type,)
|
||||
|
||||
#define QTAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).tqh_first }
|
||||
|
||||
#define Q_TAILQ_ENTRY(type, qual) \
|
||||
struct { \
|
||||
qual type *tqe_next; /* next element */ \
|
||||
qual type *qual *tqe_prev; /* address of previous next element */\
|
||||
}
|
||||
#define QTAILQ_ENTRY(type) Q_TAILQ_ENTRY(struct type,)
|
||||
|
||||
/*
|
||||
* Tail queue functions.
|
||||
*/
|
||||
#define QTAILQ_INIT(head) do { \
|
||||
(head)->tqh_first = NULL; \
|
||||
(head)->tqh_last = &(head)->tqh_first; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QTAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
|
||||
(head)->tqh_first->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(head)->tqh_first = (elm); \
|
||||
(elm)->field.tqe_prev = &(head)->tqh_first; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QTAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.tqe_next = NULL; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QTAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(listelm)->field.tqe_next = (elm); \
|
||||
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QTAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
||||
(elm)->field.tqe_next = (listelm); \
|
||||
*(listelm)->field.tqe_prev = (elm); \
|
||||
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QTAILQ_REMOVE(head, elm, field) do { \
|
||||
if (((elm)->field.tqe_next) != NULL) \
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
(elm)->field.tqe_prev; \
|
||||
else \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
#define QTAILQ_FOREACH(var, head, field) \
|
||||
for ((var) = ((head)->tqh_first); \
|
||||
(var); \
|
||||
(var) = ((var)->field.tqe_next))
|
||||
|
||||
#define QTAILQ_FOREACH_SAFE(var, head, field, next_var) \
|
||||
for ((var) = ((head)->tqh_first); \
|
||||
(var) && ((next_var) = ((var)->field.tqe_next), 1); \
|
||||
(var) = (next_var))
|
||||
|
||||
#define QTAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
||||
for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \
|
||||
(var); \
|
||||
(var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))
|
||||
|
||||
/*
|
||||
* Tail queue access methods.
|
||||
*/
|
||||
#define QTAILQ_EMPTY(head) ((head)->tqh_first == NULL)
|
||||
#define QTAILQ_FIRST(head) ((head)->tqh_first)
|
||||
#define QTAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||
|
||||
#define QTAILQ_LAST(head, headname) \
|
||||
(*(((struct headname *)((head)->tqh_last))->tqh_last))
|
||||
#define QTAILQ_PREV(elm, headname, field) \
|
||||
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
|
||||
|
||||
#endif /* !QEMU_SYS_QUEUE_H_ */
|
Loading…
Reference in New Issue