Skip to content

Commit

Permalink
added a new SDL_hint - SDL_SetHint(SDL_HINT_AUDIO_DIRECT_BUFFER_ACCES…
Browse files Browse the repository at this point in the history
…S_DC, "1");

this will allow the client callback to directly write to the audio buffer without an additional memcpy
  • Loading branch information
GPF committed Oct 28, 2024
1 parent 0d9463f commit 5df52b9
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 58 deletions.
16 changes: 16 additions & 0 deletions include/SDL_hints.h
Original file line number Diff line number Diff line change
Expand Up @@ -2176,6 +2176,21 @@ extern "C" {
*/
#define SDL_HINT_VITA_TOUCH_MOUSE_DEVICE "SDL_HINT_VITA_TOUCH_MOUSE_DEVICE"

/**
* \brief Hint that controls direct access to the audio buffer.
*
* This hint enables the client to access the audio buffer directly through
* the SDL_DC_SetSoundBuffer function, which allows writing audio data directly
* into the circular buffer. By using this hint, the client can bypass the need
* for multiple memory copies (i.e., memcpy) that are typically required to
* transfer audio data from the callback to the buffer, improving performance
* and reducing latency in audio playback.
*
* - "0": Disable direct buffer access (default).
* - "1": Enable direct buffer access.
*/
#define SDL_HINT_AUDIO_DIRECT_BUFFER_ACCESS_DC "SDL_AUDIO_DIRECT_BUFFER_ACCESS_DC"

/**
* A variable controlling whether the Android / tvOS remotes should be listed
* as joystick devices, instead of sending keyboard events.
Expand All @@ -2185,6 +2200,7 @@ extern "C" {
* - "0": Remotes send enter/escape/arrow key events
* - "1": Remotes are available as 2 axis, 2 button joysticks (the default).
*/

#define SDL_HINT_TV_REMOTE_AS_JOYSTICK "SDL_TV_REMOTE_AS_JOYSTICK"

/**
Expand Down
89 changes: 69 additions & 20 deletions src/audio/dreamcast/SDL_dreamcastaudio.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,41 @@
#include <kos/thread.h>
#include "../SDL_sysaudio.h"
#include "SDL_timer.h"
#include "SDL_hints.h"
#include "kos.h"

static SDL_AudioDevice *audioDevice = NULL; // Pointer to the active audio output device
static SDL_AudioDevice *captureDevice = NULL; // Pointer to the active audio capture device

// Helper function to provide writable buffer to the client.
// This function is now named SDL_DC_SetSoundBuffer and provides a pointer
// to the writable part of the circular buffer.
void SDL_DC_SetSoundBuffer(Uint8 **buffer_ptr, int *available_size) {
SDL_AudioDevice *device = audioDevice;
SDL_PrivateAudioData *hidden = (SDL_PrivateAudioData *)device->hidden;

// Calculate writable bytes
int writable_bytes;
if (hidden->write_index >= hidden->read_index) {
writable_bytes = hidden->buffer_size - hidden->write_index;
} else {
writable_bytes = hidden->read_index - hidden->write_index;
}

*available_size = writable_bytes;

// Return the writable part of the buffer
*buffer_ptr = (Uint8 *)hidden->buffer + hidden->write_index;
}

// Stream callback function
static void *stream_callback(snd_stream_hnd_t hnd, int req, int *done) {
SDL_AudioDevice *device = audioDevice;
SDL_PrivateAudioData *hidden = (SDL_PrivateAudioData *)device->hidden;
void *buffer = hidden->buffer; // Use hidden->buffer for interleaving
int buffer_size = device->spec.size;
Uint8 *buffer = (Uint8 *)hidden->buffer;
int buffer_size = hidden->buffer_size;
int bytes_to_copy = SDL_min(req, buffer_size);
int got = 0;
int remaining = bytes_to_copy;
SDL_AudioCallback callback;

SDL_LockMutex(device->mixer_lock);
Expand All @@ -55,40 +77,51 @@ static void *stream_callback(snd_stream_hnd_t hnd, int req, int *done) {

callback = device->callbackspec.callback;

// Handle paused or disabled audio
if (!SDL_AtomicGet(&device->enabled) || SDL_AtomicGet(&device->paused)) {
// Audio is not enabled or is paused, so clear the stream and set silence
if (device->stream) {
SDL_AudioStreamClear(device->stream);
}
SDL_memset(buffer, device->callbackspec.silence, bytes_to_copy);
} else {
SDL_assert(device->spec.size == bytes_to_copy);
if (hidden->direct_buffer_access) {
Uint8 *writable_buffer = NULL;
int writable_size = 0;

// No stream conversion needed
if (!device->stream) {
callback(device->callbackspec.userdata, buffer, bytes_to_copy);
SDL_DC_SetSoundBuffer(&writable_buffer, &writable_size);
int bytes_to_copy = SDL_min(req, writable_size);

// Call the client callback directly to fill the buffer
device->callbackspec.callback(device->callbackspec.userdata, writable_buffer, bytes_to_copy);
hidden->write_index = (hidden->write_index + bytes_to_copy) % hidden->buffer_size;


} else {
// Stream conversion is needed
while (SDL_AudioStreamAvailable(device->stream) < bytes_to_copy) {
callback(device->callbackspec.userdata, hidden->buffer, buffer_size);
if (SDL_AudioStreamPut(device->stream, hidden->buffer, buffer_size) == -1) {
SDL_AudioStreamClear(device->stream);
SDL_AtomicSet(&device->enabled, 0);
break;
// Fall back to the existing behavior with memcpy
if (!device->stream) {
callback(device->callbackspec.userdata, buffer, bytes_to_copy);
} else {
while (SDL_AudioStreamAvailable(device->stream) < bytes_to_copy) {
callback(device->callbackspec.userdata, hidden->buffer, buffer_size);
if (SDL_AudioStreamPut(device->stream, hidden->buffer, buffer_size) == -1) {
SDL_AudioStreamClear(device->stream);
SDL_AtomicSet(&device->enabled, 0);
break;
}
}
}

got = SDL_AudioStreamGet(device->stream, buffer, bytes_to_copy);
if (got != bytes_to_copy) {
SDL_memset(buffer, device->callbackspec.silence, bytes_to_copy);
int got = SDL_AudioStreamGet(device->stream, buffer, bytes_to_copy);
if (got != bytes_to_copy) {
SDL_memset(buffer, device->callbackspec.silence, bytes_to_copy);
}
}
}
}

SDL_UnlockMutex(device->mixer_lock);

*done = req;
return buffer; // Return the interleaved buffer
return buffer;
}

// Thread function for audio playback
Expand Down Expand Up @@ -119,7 +152,9 @@ int DREAMCASTAUD_OpenDevice(_THIS, const char *devname) {
SDL_PrivateAudioData *hidden;
SDL_AudioFormat test_format;
int channels,frequency;
char *hint_value;
audioDevice = _this;


hidden = (SDL_PrivateAudioData *)SDL_malloc(sizeof(*hidden));
if (!hidden) {
Expand All @@ -128,6 +163,16 @@ int DREAMCASTAUD_OpenDevice(_THIS, const char *devname) {
SDL_zerop(hidden);
_this->hidden = (struct SDL_PrivateAudioData*)hidden;

// Read the hint for direct buffer access
hint_value = SDL_GetHint(SDL_HINT_AUDIO_DIRECT_BUFFER_ACCESS_DC);
if (hint_value && SDL_strcasecmp(hint_value, "1") == 0) {
hidden->direct_buffer_access = SDL_TRUE;
SDL_Log("Direct buffer access enabled for Dreamcast audio driver.");
} else {
hidden->direct_buffer_access = SDL_FALSE;
SDL_Log("Direct buffer access disabled for Dreamcast audio driver.");
}

// Initialize sound stream system
if (snd_stream_init() != 0) {
SDL_free(hidden);
Expand All @@ -151,6 +196,10 @@ int DREAMCASTAUD_OpenDevice(_THIS, const char *devname) {

SDL_CalculateAudioSpec(&_this->spec);

hidden->buffer_size = _this->spec.size; // The size remains the same
hidden->read_index = 0; // Initialize read index
hidden->write_index = 0; // Initialize write index

// Allocate the stream with the data callback
hidden->stream_handle = snd_stream_alloc(NULL, _this->spec.size);
if (hidden->stream_handle == SND_STREAM_INVALID) {
Expand Down
5 changes: 4 additions & 1 deletion src/audio/dreamcast/SDL_dreamcastaudio.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@
typedef struct {
snd_stream_hnd_t stream_handle; // Handle for the audio stream
void *buffer; // Buffer for audio data
int buffer_size; // Total size of the buffer
int read_index; // Read index for the circular buffer
int write_index; // Write index for the circular buffer
SDL_bool direct_buffer_access; // Flag to enable direct buffer access
} SDL_PrivateAudioData;


#endif /* SDL_dreamcastaudio_h_ */
/* vi: set ts=4 sw=4 expandtab: */
95 changes: 70 additions & 25 deletions src/render/opengl/SDL_render_gl.c
Original file line number Diff line number Diff line change
Expand Up @@ -426,8 +426,13 @@ convert_format(GL_RenderData *renderdata, Uint32 pixel_format,
case SDL_PIXELFORMAT_ARGB1555:
*internalFormat = GL_RGBA;
*format = GL_BGRA;
*type = GL_UNSIGNED_SHORT_1_5_5_5_REV; // // Dreamcast-specific handling for ARGB1555
*type = GL_UNSIGNED_SHORT_1_5_5_5_REV; // Dreamcast-specific handling for ARGB1555
break;
case SDL_PIXELFORMAT_ARGB4444:
*internalFormat = GL_RGBA;
*format = GL_BGRA;
*type = GL_UNSIGNED_SHORT_4_4_4_4_REV; // Dreamcast-specific handling for ARGB8888
break;
case SDL_PIXELFORMAT_ARGB8888:
*internalFormat = GL_RGBA;
*format = GL_RGBA;
Expand Down Expand Up @@ -518,19 +523,7 @@ static int GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
SDL_free(data);
return SDL_OutOfMemory();
}
#ifdef __DREAMCAST__
// // Remove the color key setup or modify it to handle alpha correctly
// if (texture->format == SDL_PIXELFORMAT_ARGB8888) {
// // If you still want to use color keying for some reason:
// // Uint32 transparentColor = *(Uint32 *)data->pixels; // Get the transparent color
// // SDL_SetColorKey(data->pixels, SDL_TRUE, transparentColor);
// SDL_Log("Dreamcast: ARGB8888 texture, using alpha channel for transparency.\n");
// } else if (texture->format == SDL_PIXELFORMAT_RGB888) {
// Uint32 transparentColor = *(Uint32 *)data->pixels; // Get the transparent color
// SDL_SetColorKey(data->pixels, SDL_TRUE, transparentColor);
// SDL_Log("Dreamcast: RGB888 texture, ensure conversion to ARGB8888 for proper blending.\n");
// }
#endif

}

if (texture->access == SDL_TEXTUREACCESS_TARGET) {
Expand Down Expand Up @@ -563,14 +556,20 @@ static int GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
} else {
#ifdef __DREAMCAST__
GLint maxSize;
int isPowerOfTwoWidth;
int isPowerOfTwoHeight;


renderdata->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);

int isPowerOfTwoWidth = (texture->w & (texture->w - 1)) == 0;
int isPowerOfTwoHeight = (texture->h & (texture->h - 1)) == 0;
isPowerOfTwoWidth = (texture->w & (texture->w - 1)) == 0;
isPowerOfTwoHeight = (texture->h & (texture->h - 1)) == 0;

if (!isPowerOfTwoWidth || !isPowerOfTwoHeight) {
int oldtexture_w = texture->w;
int oldtexture_h = texture->h;
int texturebpp;
int newStride;
texture_w = SDL_powerof2(texture->w);
texture_h = SDL_powerof2(texture->h);
texture_w = (texture_w > maxSize) ? maxSize : texture_w;
Expand All @@ -583,8 +582,8 @@ static int GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)

texture->scaleMode = SDL_ScaleModeNearest;

const int texturebpp = SDL_BYTESPERPIXEL(texture->format);
int newStride = texture_w * texturebpp;
texturebpp = SDL_BYTESPERPIXEL(texture->format);
newStride = texture_w * texturebpp;
renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, (newStride / texturebpp));
} else {
texture_w = texture->w;
Expand All @@ -593,7 +592,27 @@ static int GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
data->texh = 1.0f;
SDL_Log("Dreamcast: Texture size is power-of-two: w=%d, h=%d\n", texture_w, texture_h);
}

// Adjust pixel data only if the format is RGB888
// if (texture->format == SDL_PIXELFORMAT_RGB888) {
// SDL_SetColorKey(data->pixels, 1, *((Uint8 *)data->pixels));
// Uint32 *pixels = (Uint32 *)data->pixels;
// for (int y = 0; y < texture_h; ++y) {
// for (int x = 0; x < texture_w; ++x) {
// Uint32 pixel = pixels[y * texture_w + x];

// // Extract ARGB values
// Uint8 a = (pixel >> 24) & 0xFF;
// Uint8 r = (pixel >> 16) & 0xFF;
// Uint8 g = (pixel >> 8) & 0xFF;
// Uint8 b = pixel & 0xFF;

// // Swap B and R
// Uint32 corrected_pixel = (a << 24) | (b << 16) | (g << 8) | r;
// pixels[y * texture_w + x] = corrected_pixel;
// }
// }
// SDL_Log("Dreamcast: RGB888 texture, ensure conversion to ARGB8888 for proper blending.\n");
// }
#else
texture_w = SDL_powerof2(texture->w);
texture_h = SDL_powerof2(texture->h);
Expand Down Expand Up @@ -777,12 +796,38 @@ static int GL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
renderdata->glBindTexture(textype, data->texture);
renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, (pitch / texturebpp));
#ifdef __DREAMCAST__
// SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
// "Dreamcast: glTexSubImage2D - textype=%d, level=0, rect->x=%d, rect->y=%d, adjustedWidth=%d, adjustedHeight=%d, format=%d, formattype=%d, pixels=%p\n",
// textype, rect->x, rect->y, adjustedWidth, adjustedHeight, data->format, data->formattype, pixels);

#endif
// #ifdef __DREAMCAST__
// // SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
// // "Dreamcast: glTexSubImage2D - textype=%d, level=0, rect->x=%d, rect->y=%d, adjustedWidth=%d, adjustedHeight=%d, format=%d, formattype=%d, pixels=%p\n",
// // textype, rect->x, rect->y, adjustedWidth, adjustedHeight, data->format, data->formattype, pixels);
// // Remove the color key setup or modify it to handle alpha correctly
// if (texture->format == SDL_PIXELFORMAT_ARGB8888) {
// // Get texture dimensions
// int texture_w = texture->w;
// int texture_h = texture->h;

// // Convert RGB888 pixels to ARGB8888 with swapped red and blue channels
// Uint32 *thepixels = (Uint32 *)pixels;
// for (int y = 0; y < texture_h; ++y) {
// for (int x = 0; x < texture_w; ++x) {
// Uint32 cpixel = thepixels[y * texture_w + x];

// // Extract RGB values
// Uint8 r = (cpixel >> 16) & 0xFF;
// Uint8 g = (cpixel >> 8) & 0xFF;
// Uint8 b = cpixel & 0xFF;

// // Set alpha to 0xFF (fully opaque)
// Uint8 a = 0xFF;

// // Create corrected pixel with swapped red and blue channels
// Uint32 corrected_pixel = (a << 24) | (b << 16) | (g << 8) | r;
// thepixels[y * texture_w + x] = corrected_pixel;
// }
// }
// SDL_Log("Dreamcast: Converted RGB888 texture to ARGB8888 for proper blending.\n");
// }
// #endif
renderdata->glTexSubImage2D(textype, 0, rect->x, rect->y, rect->w,
rect->h, data->format, data->formattype,
pixels);
Expand Down
2 changes: 1 addition & 1 deletion src/video/dreamcast/SDL_dreamcastvideo.c
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ int DREAMCAST_VideoInit(_THIS) {
int disp_mode;
int pixel_mode;
int width = 640, height = 480; // Default to 640x480, modify based on requirements
int bpp = 16; // Bits per pixel
int bpp = 32; // Bits per pixel
Uint32 Rmask, Gmask, Bmask;

SDL_zero(current_mode);
Expand Down
5 changes: 5 additions & 0 deletions test/dreamcast/test/nehe06/nehe06.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ SDL_Surface *LoadBMP(char *filename) {
fprintf(stderr, "Unable to load %s: %s\n", filename, SDL_GetError());
return NULL;
}
const char* format_name = SDL_GetPixelFormatName(image->format->format);
printf("Image surface format: %s\n", format_name);

printf("SDL_LoadBMP_RW\n");
SDL_SetColorKey(image, 1, *((Uint8 *)image->pixels));

tmpbuf = (Uint8 *)malloc(image->pitch);
if (!tmpbuf) {
fprintf(stderr, "Out of memory\n");
Expand Down
Loading

0 comments on commit 5df52b9

Please sign in to comment.