mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 14:19:46 -06:00
Linux: Alsa backend for the audio plugins. Didn't succeed in reducing latency though, but hopefully will work better than libao - shouldn't hang weirdly at underruns, I hope, like libao does.
git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@4240 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
@ -64,8 +64,7 @@ public:
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
public:
|
public:
|
||||||
AOSound(CMixer *mixer) :
|
AOSound(CMixer *mixer) : SoundStream(mixer) {}
|
||||||
SoundStream(mixer) {}
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
195
Source/Core/AudioCommon/Src/AlsaSoundStream.cpp
Normal file
195
Source/Core/AudioCommon/Src/AlsaSoundStream.cpp
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
// Copyright (C) 2003 Dolphin Project.
|
||||||
|
|
||||||
|
// 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, version 2.0.
|
||||||
|
|
||||||
|
// 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 2.0 for more details.
|
||||||
|
|
||||||
|
// A copy of the GPL 2.0 should have been included with the program.
|
||||||
|
// If not, see http://www.gnu.org/licenses/
|
||||||
|
|
||||||
|
// Official SVN repository and contact information can be found at
|
||||||
|
// http://code.google.com/p/dolphin-emu/
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
#include "Thread.h"
|
||||||
|
|
||||||
|
#include "AlsaSoundStream.h"
|
||||||
|
|
||||||
|
#define BUFFER_SIZE 4096
|
||||||
|
#define BUFFER_SIZE_BYTES (BUFFER_SIZE*2*2)
|
||||||
|
|
||||||
|
AlsaSound::AlsaSound(CMixer *mixer) : SoundStream(mixer), thread_data(0), handle(NULL)
|
||||||
|
{
|
||||||
|
mix_buffer = new u8[BUFFER_SIZE_BYTES];
|
||||||
|
}
|
||||||
|
|
||||||
|
AlsaSound::~AlsaSound()
|
||||||
|
{
|
||||||
|
delete [] mix_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *ThreadTrampoline(void *args)
|
||||||
|
{
|
||||||
|
reinterpret_cast<AlsaSound *>(args)->SoundLoop();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AlsaSound::Start()
|
||||||
|
{
|
||||||
|
thread = new Common::Thread(&ThreadTrampoline, this);
|
||||||
|
thread_data = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlsaSound::Stop()
|
||||||
|
{
|
||||||
|
thread_data = 1;
|
||||||
|
delete thread;
|
||||||
|
thread = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlsaSound::Update()
|
||||||
|
{
|
||||||
|
// don't need to do anything here.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called on audio thread.
|
||||||
|
void AlsaSound::SoundLoop()
|
||||||
|
{
|
||||||
|
AlsaInit();
|
||||||
|
while (!thread_data)
|
||||||
|
{
|
||||||
|
int frames_to_deliver = 512;
|
||||||
|
m_mixer->Mix(reinterpret_cast<short *>(mix_buffer), frames_to_deliver);
|
||||||
|
int rc = snd_pcm_writei(handle, mix_buffer, frames_to_deliver);
|
||||||
|
if (rc == -EPIPE)
|
||||||
|
{
|
||||||
|
// Underrun
|
||||||
|
snd_pcm_prepare(handle);
|
||||||
|
}
|
||||||
|
else if (rc < 0)
|
||||||
|
{
|
||||||
|
ERROR_LOG(AUDIO, "writei fail: %s", snd_strerror(rc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AlsaShutdown();
|
||||||
|
thread_data = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AlsaSound::AlsaInit()
|
||||||
|
{
|
||||||
|
unsigned int sample_rate = m_mixer->GetSampleRate();
|
||||||
|
int err;
|
||||||
|
int dir;
|
||||||
|
snd_pcm_sw_params_t *swparams;
|
||||||
|
snd_pcm_hw_params_t *hwparams;
|
||||||
|
|
||||||
|
err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
ERROR_LOG(AUDIO, "Audio open error: %s\n", snd_strerror(err));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_pcm_hw_params_alloca(&hwparams);
|
||||||
|
|
||||||
|
err = snd_pcm_hw_params_any(handle, hwparams);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
ERROR_LOG(AUDIO, "Broken configuration for this PCM: %s\n", snd_strerror(err));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
ERROR_LOG(AUDIO, "Access type not available: %s\n", snd_strerror(err));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
ERROR_LOG(AUDIO, "Sample format not available: %s\n", snd_strerror(err));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is weird - if I do pass in a pointer to a variable, like the header wants me to,
|
||||||
|
// the sample rate goes mad. It seems that the alsa header doesn't match the library we link in :(
|
||||||
|
// If anyone know why, i'd appreciate if you let me know - ector.
|
||||||
|
err = snd_pcm_hw_params_set_rate_near(handle, hwparams, (unsigned int *)sample_rate, &dir);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
ERROR_LOG(AUDIO, "Rate not available: %s\n", snd_strerror(err));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = snd_pcm_hw_params_set_channels(handle, hwparams, 2);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
ERROR_LOG(AUDIO, "Channels count not available: %s\n", snd_strerror(err));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = snd_pcm_hw_params(handle, hwparams);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
ERROR_LOG(AUDIO, "Unable to install hw params: %s\n", snd_strerror(err));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_pcm_sw_params_alloca(&swparams);
|
||||||
|
|
||||||
|
err = snd_pcm_sw_params_current(handle, swparams);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
ERROR_LOG(AUDIO, "cannot init sw params: %s\n", snd_strerror(err));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = snd_pcm_sw_params_set_avail_min(handle, swparams, BUFFER_SIZE);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
ERROR_LOG(AUDIO, "cannot set avail min: %s\n", snd_strerror(err));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = snd_pcm_sw_params_set_start_threshold(handle, swparams, 0U);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
ERROR_LOG(AUDIO, "cannot set start thresh: %s\n", snd_strerror(err));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = snd_pcm_sw_params(handle, swparams);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
ERROR_LOG(AUDIO, "cannot set sw params: %s\n", snd_strerror(err));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = snd_pcm_prepare(handle);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
ERROR_LOG(AUDIO, "Unable to prepare: %s\n", snd_strerror(err));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
NOTICE_LOG(AUDIO, "ALSA successfully initialized.\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlsaSound::AlsaShutdown()
|
||||||
|
{
|
||||||
|
if (handle != NULL)
|
||||||
|
{
|
||||||
|
snd_pcm_drop(handle);
|
||||||
|
snd_pcm_close(handle);
|
||||||
|
handle = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
69
Source/Core/AudioCommon/Src/AlsaSoundStream.h
Normal file
69
Source/Core/AudioCommon/Src/AlsaSoundStream.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// Copyright (C) 2003 Dolphin Project.
|
||||||
|
|
||||||
|
// 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, version 2.0.
|
||||||
|
|
||||||
|
// 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 2.0 for more details.
|
||||||
|
|
||||||
|
// A copy of the GPL 2.0 should have been included with the program.
|
||||||
|
// If not, see http://www.gnu.org/licenses/
|
||||||
|
|
||||||
|
// Official SVN repository and contact information can be found at
|
||||||
|
// http://code.google.com/p/dolphin-emu/
|
||||||
|
|
||||||
|
#ifndef _ALSA_SOUND_STREAM_H
|
||||||
|
#define _ALSA_SOUND_STREAM_H
|
||||||
|
|
||||||
|
#include <alsa/asoundlib.h>
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
#include "SoundStream.h"
|
||||||
|
|
||||||
|
#include "Thread.h"
|
||||||
|
|
||||||
|
#define HAVE_ALSA 1
|
||||||
|
|
||||||
|
class AlsaSound : public SoundStream
|
||||||
|
{
|
||||||
|
#if defined(HAVE_ALSA) && HAVE_ALSA
|
||||||
|
public:
|
||||||
|
AlsaSound(CMixer *mixer);
|
||||||
|
virtual ~AlsaSound();
|
||||||
|
|
||||||
|
virtual bool Start();
|
||||||
|
virtual void SoundLoop();
|
||||||
|
virtual void Stop();
|
||||||
|
|
||||||
|
static bool isValid() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
virtual bool usesMixer() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool AlsaInit();
|
||||||
|
void AlsaShutdown();
|
||||||
|
|
||||||
|
u8 *mix_buffer;
|
||||||
|
Common::Thread *thread;
|
||||||
|
// 0 = continue
|
||||||
|
// 1 = shutdown
|
||||||
|
// 2 = done shutting down.
|
||||||
|
volatile int thread_data;
|
||||||
|
|
||||||
|
snd_pcm_t *handle;
|
||||||
|
#else
|
||||||
|
public:
|
||||||
|
AlsaSound(CMixer *mixer) : SoundStream(mixer) {}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -19,6 +19,7 @@
|
|||||||
#include "Mixer.h"
|
#include "Mixer.h"
|
||||||
#include "DSoundStream.h"
|
#include "DSoundStream.h"
|
||||||
#include "AOSoundStream.h"
|
#include "AOSoundStream.h"
|
||||||
|
#include "AlsaSoundStream.h"
|
||||||
#include "NullSoundStream.h"
|
#include "NullSoundStream.h"
|
||||||
#include "OpenALStream.h"
|
#include "OpenALStream.h"
|
||||||
|
|
||||||
@ -26,18 +27,22 @@ namespace AudioCommon
|
|||||||
{
|
{
|
||||||
SoundStream *InitSoundStream(CMixer *mixer)
|
SoundStream *InitSoundStream(CMixer *mixer)
|
||||||
{
|
{
|
||||||
|
// This looks evil.
|
||||||
if (!mixer)
|
if (!mixer)
|
||||||
mixer = new CMixer();
|
mixer = new CMixer();
|
||||||
|
|
||||||
std::string backend = ac_Config.sBackend;
|
std::string backend = ac_Config.sBackend;
|
||||||
if (backend == BACKEND_DIRECTSOUND && DSound::isValid()) soundStream = new DSound(mixer, g_dspInitialize.hWnd);
|
if (backend == BACKEND_DIRECTSOUND && DSound::isValid()) soundStream = new DSound(mixer, g_dspInitialize.hWnd);
|
||||||
if (backend == BACKEND_AOSOUND && AOSound::isValid()) soundStream = new AOSound(mixer);
|
if (backend == BACKEND_AOSOUND && AOSound::isValid()) soundStream = new AOSound(mixer);
|
||||||
if (backend == BACKEND_OPENAL && OpenALStream::isValid()) soundStream = new OpenALStream(mixer);
|
if (backend == BACKEND_OPENAL && OpenALStream::isValid()) soundStream = new OpenALStream(mixer);
|
||||||
if (backend == BACKEND_NULL && NullSound::isValid()) soundStream = new NullSound(mixer);
|
if (backend == BACKEND_ALSA && AlsaSound::isValid()) soundStream = new AlsaSound(mixer);
|
||||||
|
if (backend == BACKEND_NULL && NullSound::isValid()) soundStream = new NullSound(mixer);
|
||||||
if (soundStream != NULL) {
|
|
||||||
|
if (soundStream != NULL)
|
||||||
|
{
|
||||||
ac_Config.Update();
|
ac_Config.Update();
|
||||||
if (soundStream->Start()) {
|
if (soundStream->Start())
|
||||||
|
{
|
||||||
// Start the sound recording
|
// Start the sound recording
|
||||||
/*
|
/*
|
||||||
if (ac_Config.record) {
|
if (ac_Config.record) {
|
||||||
@ -81,6 +86,7 @@ namespace AudioCommon
|
|||||||
if (DSound::isValid()) backends.push_back(BACKEND_DIRECTSOUND);
|
if (DSound::isValid()) backends.push_back(BACKEND_DIRECTSOUND);
|
||||||
if (AOSound::isValid()) backends.push_back(BACKEND_AOSOUND);
|
if (AOSound::isValid()) backends.push_back(BACKEND_AOSOUND);
|
||||||
if (OpenALStream::isValid()) backends.push_back(BACKEND_OPENAL);
|
if (OpenALStream::isValid()) backends.push_back(BACKEND_OPENAL);
|
||||||
|
if (AlsaSound::isValid()) backends.push_back(BACKEND_ALSA);
|
||||||
if (NullSound::isValid()) backends.push_back(BACKEND_NULL);
|
if (NullSound::isValid()) backends.push_back(BACKEND_NULL);
|
||||||
|
|
||||||
return backends;
|
return backends;
|
||||||
|
@ -25,7 +25,9 @@
|
|||||||
#define BACKEND_DIRECTSOUND "DSound"
|
#define BACKEND_DIRECTSOUND "DSound"
|
||||||
#define BACKEND_AOSOUND "AOSound"
|
#define BACKEND_AOSOUND "AOSound"
|
||||||
#define BACKEND_OPENAL "OpenAL"
|
#define BACKEND_OPENAL "OpenAL"
|
||||||
|
#define BACKEND_ALSA "ALSA"
|
||||||
#define BACKEND_NULL "NullSound"
|
#define BACKEND_NULL "NullSound"
|
||||||
|
|
||||||
struct AudioCommonConfig
|
struct AudioCommonConfig
|
||||||
{
|
{
|
||||||
bool m_EnableDTKMusic;
|
bool m_EnableDTKMusic;
|
||||||
|
@ -17,5 +17,8 @@ if acenv['HAVE_OPENAL']:
|
|||||||
|
|
||||||
if acenv['HAVE_AO']:
|
if acenv['HAVE_AO']:
|
||||||
files += [ 'AOSoundStream.cpp' ]
|
files += [ 'AOSoundStream.cpp' ]
|
||||||
|
|
||||||
|
# TODO: make conditional?
|
||||||
|
files += [ 'AlsaSoundStream.cpp' ]
|
||||||
|
|
||||||
acenv.StaticLibrary(env['local_libs'] + 'audiocommon', files)
|
acenv.StaticLibrary(env['local_libs'] + 'audiocommon', files)
|
||||||
|
@ -84,6 +84,7 @@
|
|||||||
#define HAVE_WX 1
|
#define HAVE_WX 1
|
||||||
#define HAVE_SFML 1
|
#define HAVE_SFML 1
|
||||||
#define HAVE_OPENAL 1
|
#define HAVE_OPENAL 1
|
||||||
|
#define HAVE_ALSA 1
|
||||||
|
|
||||||
// it is VERY DANGEROUS to mix _SECURE_SCL=0 and _SECURE_SCL=1 compiled libraries.
|
// it is VERY DANGEROUS to mix _SECURE_SCL=0 and _SECURE_SCL=1 compiled libraries.
|
||||||
// You will get bizarre crash bugs whenever you use STL.
|
// You will get bizarre crash bugs whenever you use STL.
|
||||||
@ -110,6 +111,11 @@
|
|||||||
#define POSIX 1
|
#define POSIX 1
|
||||||
#define MAX_PATH 260
|
#define MAX_PATH 260
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#define HAVE_ALSA 0
|
||||||
|
#else
|
||||||
|
#define HAVE_ALSA 1
|
||||||
|
#endif
|
||||||
// Windows compatibility
|
// Windows compatibility
|
||||||
#define __forceinline inline __attribute__((always_inline))
|
#define __forceinline inline __attribute__((always_inline))
|
||||||
|
|
||||||
|
@ -120,10 +120,12 @@ void CopyEFB(const BPCmd &bp, const EFBRectangle &rc, const u32 &address, const
|
|||||||
{
|
{
|
||||||
// bpmem.zcontrol.pixel_format to PIXELFMT_Z24 is when the game wants to copy from ZBuffer (Zbuffer uses 24-bit Format)
|
// bpmem.zcontrol.pixel_format to PIXELFMT_Z24 is when the game wants to copy from ZBuffer (Zbuffer uses 24-bit Format)
|
||||||
if (!g_Config.bEFBCopyDisable)
|
if (!g_Config.bEFBCopyDisable)
|
||||||
|
{
|
||||||
if (g_Config.bCopyEFBToRAM) // To RAM
|
if (g_Config.bCopyEFBToRAM) // To RAM
|
||||||
TextureConverter::EncodeToRam(address, fromZBuffer, isIntensityFmt, copyfmt, scaleByHalf, rc);
|
TextureConverter::EncodeToRam(address, fromZBuffer, isIntensityFmt, copyfmt, scaleByHalf, rc);
|
||||||
else // To OGL Texture
|
else // To OGL Texture
|
||||||
TextureMngr::CopyRenderTargetToTexture(address, fromZBuffer, isIntensityFmt, copyfmt, scaleByHalf, rc);
|
TextureMngr::CopyRenderTargetToTexture(address, fromZBuffer, isIntensityFmt, copyfmt, scaleByHalf, rc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderToXFB(const BPCmd &bp, const EFBRectangle &rc, const float &yScale, const float &xfbLines, u32 xfbAddr, const u32 &dstWidth, const u32 &dstHeight)
|
void RenderToXFB(const BPCmd &bp, const EFBRectangle &rc, const float &yScale, const float &xfbLines, u32 xfbAddr, const u32 &dstWidth, const u32 &dstHeight)
|
||||||
|
@ -75,6 +75,8 @@ struct CachedDisplayList
|
|||||||
frame_count = frameCount;
|
frame_count = frameCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool uncachable; // if set, this DL will always be interpreted. This gets set if hash ever changes.
|
||||||
|
|
||||||
int pass;
|
int pass;
|
||||||
u32 dl_hash;
|
u32 dl_hash;
|
||||||
|
|
||||||
@ -87,8 +89,6 @@ struct CachedDisplayList
|
|||||||
|
|
||||||
int frame_count;
|
int frame_count;
|
||||||
|
|
||||||
bool uncachable; // if set, this DL will always be interpreted. This gets set if hash ever changes.
|
|
||||||
|
|
||||||
// ... Something containing cached vertex buffers here ...
|
// ... Something containing cached vertex buffers here ...
|
||||||
|
|
||||||
// Compile the commands themselves down to native code.
|
// Compile the commands themselves down to native code.
|
||||||
@ -159,12 +159,12 @@ bool AnalyzeAndRunDisplayList(u32 address, int size, CachedDisplayList *dl)
|
|||||||
// Execute
|
// Execute
|
||||||
u32 Cmd2 = DataReadU32();
|
u32 Cmd2 = DataReadU32();
|
||||||
int transfer_size = ((Cmd2 >> 16) & 15) + 1;
|
int transfer_size = ((Cmd2 >> 16) & 15) + 1;
|
||||||
u32 address = Cmd2 & 0xFFFF;
|
u32 xf_address = Cmd2 & 0xFFFF;
|
||||||
// TODO - speed this up. pshufb?
|
// TODO - speed this up. pshufb?
|
||||||
u32 data_buffer[16];
|
u32 data_buffer[16];
|
||||||
for (int i = 0; i < transfer_size; i++)
|
for (int i = 0; i < transfer_size; i++)
|
||||||
data_buffer[i] = DataReadU32();
|
data_buffer[i] = DataReadU32();
|
||||||
LoadXFReg(transfer_size, address, data_buffer);
|
LoadXFReg(transfer_size, xf_address, data_buffer);
|
||||||
INCSTAT(stats.thisFrame.numXFLoads);
|
INCSTAT(stats.thisFrame.numXFLoads);
|
||||||
|
|
||||||
// Analyze
|
// Analyze
|
||||||
@ -311,13 +311,13 @@ bool CompileAndRunDisplayList(u32 address, int size, CachedDisplayList *dl)
|
|||||||
// Execute
|
// Execute
|
||||||
u32 Cmd2 = DataReadU32();
|
u32 Cmd2 = DataReadU32();
|
||||||
int transfer_size = ((Cmd2 >> 16) & 15) + 1;
|
int transfer_size = ((Cmd2 >> 16) & 15) + 1;
|
||||||
u32 address = Cmd2 & 0xFFFF;
|
u32 xf_address = Cmd2 & 0xFFFF;
|
||||||
// TODO - speed this up. pshufb?
|
// TODO - speed this up. pshufb?
|
||||||
u8 *real_data_buffer = AllocStaticData(4 * transfer_size);
|
u8 *real_data_buffer = AllocStaticData(4 * transfer_size);
|
||||||
u32 *data_buffer = (u32 *)real_data_buffer;
|
u32 *data_buffer = (u32 *)real_data_buffer;
|
||||||
for (int i = 0; i < transfer_size; i++)
|
for (int i = 0; i < transfer_size; i++)
|
||||||
data_buffer[i] = DataReadU32();
|
data_buffer[i] = DataReadU32();
|
||||||
LoadXFReg(transfer_size, address, data_buffer);
|
LoadXFReg(transfer_size, xf_address, data_buffer);
|
||||||
INCSTAT(stats.thisFrame.numXFLoads);
|
INCSTAT(stats.thisFrame.numXFLoads);
|
||||||
|
|
||||||
// Compile
|
// Compile
|
||||||
|
Reference in New Issue
Block a user