Skip to content

Instantly share code, notes, and snippets.

@startergo
Forked from akihikodaki/README.en.md
Last active December 26, 2025 17:33
Show Gist options
  • Select an option

  • Save startergo/0d9a7425876c2b42f8b797af80fbe3d8 to your computer and use it in GitHub Desktop.

Select an option

Save startergo/0d9a7425876c2b42f8b797af80fbe3d8 to your computer and use it in GitHub Desktop.
Linux Desktop on Apple Silicon in Practice

Linux Desktop on Apple Silicon in Practice

I bought M1 MacBook Air. It is the fastest computer I have, and I have been a GNOME/GNU/Linux user for long time. It is obvious conclusion that I need practical Linux desktop environment on Apple Silicon.

Fortunately, Linux already works on Apple Silicon/M1. But how practical is it?

  • Two native ports exist.
  • QEMU can run code on CPU natively. But what about GPU? Unfortunately, QEMU is also not optimized so much for macOS.

As I needed Linux desktop right now, I decided to hack QEMU. The most difficult challenge is obviously accelerated graphics, but there is Virgil 3D; a bridge to expose host OpenGL to the guest. https://virgil3d.github.io

It unfortunately didn't work on macOS host. So I just made it work. That's it. Here is a video demonstrating OpenGL on Linux on Apple Silicon/M1:

https://www.youtube.com/watch?v=k0bVlVQU2JQ&list=PLesZxBYUPr3wdU3sONUv4Q7UDOg1Dn_Ue&index=4

Modifications

QEMU

ui/cocoa

  • Added OpenGL support.
  • Enforced pixel by pixel display.
  • Added cursor composition.
  • Improved key mappings (e.g. Japanese IME keys, 2021-06-17)

hw/block

  • File locking on macOS is fixed. (2021-07-07, Add file.locking=on to drive to prevent drive breakage in case you concurrently launch the same virtual machine by mistake.)

coreaudio

  • Fix device change (2022-02-26)

Virgil 3D renderer

Improved OpenGL ES support.

Do It Yourself

Setup

1. Open a terminal.

2. Install MacPorts prerequisites:

sudo port install gn ninja rapidjson meson libpixman pkgconfig spice-protocol

3. Make an empty directory and change the working directory to it.

4. For Apple Silicon (ARM):

curl -L https://gist.github.com/startergo/0d9a7425876c2b42f8b797af80fbe3d8/raw/run-arm.sh | bash -

5.

bin/qemu-img create var/virtio.raw 64G

It doesn't consume the physical space until it has data, so you can make the image very large. However, you will see odd behavior if you try to write data more than the physical disk allows.

6. Apple Silicon

curl -LO https://dl01.fedoraproject.org/pub/fedora/linux/releases/41/Silverblue/aarch64/iso/Fedora-Silverblue-ostree-aarch64-41-1.4.iso

7. Apple Silicon

./run -cdrom Fedora-Silverblue-ostree-aarch64-41-1.4.iso

Proceed the installation process, and now you can run Fedora by executing ./run.

Note: you won't get keyboard to work before Linux boots because TianoCore, the UEFI firmware does not support virtio-keyboard used in this configuration.

Updating

Just download the latest run.sh and execute it in your workspace directory.

Choosing OpenGL profile

Edit run.

  • gl=off will disable Virgil 3D GPU. Most stable but laggy.
  • gl=core will enable OpenGL.framework. Unstable.
  • gl=es will enable ANGLE. Stable and fast.

Upstreaming

Upstreaming is in progress. Hopefully the features I implemented will work just by running brew install qemu in the future.

Some insights

QEMU

This QEMU modification is not secure. The graphics acceleration code lives in the same process with everything else of the virtual machine and it is huge; the graphics stack involves LLVM for shader compilation, and a simple bug in the stack can lead to complete takeover of the guest.

vhost-user-gpu provides graphics acceleration isolation, but it needs modifications to run outside Linux because:

  • historically, vhost-user is a re-implementation of Linux kernel's vhost interface, and it relies on kernel headers for interface definitions and
  • vhost-user uses eventfd which is only available on Linux.

It shouldn't be difficult, but I'm satisfied even without process isolation so I don't. The graphics acceleration process would be still shared and it would remain possible that one graphical process exploit leads to disclosure of the entire graphics output anyway.

Linux desktop on Apple Silicon/M1 in general

As I described here, such a virtualization software is practical and efficient approach to run Linux desktop. The performance overhead is also acceptable for daily use, and it even provides better integration of Linux and macOS. For example, you can switch macOS and Linux with three-finger gesture on trackpad. You can use VirtFS.

However, there are complexities that such a virtualization adds. It basically means sharing one hardware with two systems, so you have to allocate the resource properly or it ends up with bad user experience. The allocation problem happens everywhere (HID like keyboard, computing resource like CPU, power management, etc.). This approach is efficient but not the best.

In long term, native Linux port is the best option. Asahi Linux is promising and you may find it favorable than my modified QEMU for your use case even today.

diff --git a/audio/audio.c b/audio/audio.c
index 89f091b..20d4e3c 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -1415,12 +1415,18 @@ void audio_run(AudioState *s, const char *msg)
#endif
}
+void audio_generic_initialize_buffer_in(HWVoiceIn *hw)
+{
+ g_free(hw->buf_emul);
+ hw->size_emul = hw->samples * hw->info.bytes_per_frame;
+ hw->buf_emul = g_malloc(hw->size_emul);
+ hw->pos_emul = hw->pending_emul = 0;
+}
+
void audio_generic_run_buffer_in(HWVoiceIn *hw)
{
if (unlikely(!hw->buf_emul)) {
- hw->size_emul = hw->samples * hw->info.bytes_per_frame;
- hw->buf_emul = g_malloc(hw->size_emul);
- hw->pos_emul = hw->pending_emul = 0;
+ audio_generic_initialize_buffer_in(hw);
}
while (hw->pending_emul < hw->size_emul) {
@@ -1454,6 +1460,14 @@ void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
hw->pending_emul -= size;
}
+void audio_generic_initialize_buffer_out(HWVoiceOut *hw)
+{
+ g_free(hw->buf_emul);
+ hw->size_emul = hw->samples * hw->info.bytes_per_frame;
+ hw->buf_emul = g_malloc(hw->size_emul);
+ hw->pos_emul = hw->pending_emul = 0;
+}
+
size_t audio_generic_buffer_get_free(HWVoiceOut *hw)
{
if (hw->buf_emul) {
@@ -1485,9 +1499,7 @@ void audio_generic_run_buffer_out(HWVoiceOut *hw)
void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size)
{
if (unlikely(!hw->buf_emul)) {
- hw->size_emul = hw->samples * hw->info.bytes_per_frame;
- hw->buf_emul = g_malloc(hw->size_emul);
- hw->pos_emul = hw->pending_emul = 0;
+ audio_generic_initialize_buffer_out(hw);
}
*size = MIN(hw->size_emul - hw->pending_emul,
diff --git a/audio/audio_int.h b/audio/audio_int.h
index f78ca05..73172b3 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -187,9 +187,11 @@ struct audio_pcm_ops {
void (*volume_in)(HWVoiceIn *hw, Volume *vol);
};
+void audio_generic_initialize_buffer_in(HWVoiceIn *hw);
void audio_generic_run_buffer_in(HWVoiceIn *hw);
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size);
void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size);
+void audio_generic_initialize_buffer_out(HWVoiceOut *hw);
void audio_generic_run_buffer_out(HWVoiceOut *hw);
size_t audio_generic_buffer_get_free(HWVoiceOut *hw);
void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size);
diff --git a/audio/coreaudio.m b/audio/coreaudio.m
index cadd729..6ca5c03 100644
--- a/audio/coreaudio.m
+++ b/audio/coreaudio.m
@@ -33,37 +33,37 @@
#define AUDIO_CAP "coreaudio"
#include "audio_int.h"
-typedef struct coreaudioVoiceOut {
+typedef struct CoreaudioVoiceOut {
HWVoiceOut hw;
pthread_mutex_t buf_mutex;
- AudioDeviceID outputDeviceID;
- int frameSizeSetting;
- uint32_t bufferCount;
- UInt32 audioDevicePropertyBufferFrameSize;
+ AudioDeviceID device_id;
+ int frame_size_setting;
+ uint32_t buffer_count;
+ UInt32 device_frame_size;
AudioDeviceIOProcID ioprocid;
bool enabled;
-} coreaudioVoiceOut;
+} CoreaudioVoiceOut;
-static const AudioObjectPropertyAddress voice_addr = {
+static const AudioObjectPropertyAddress voice_out_addr = {
kAudioHardwarePropertyDefaultOutputDevice,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMain
};
-static OSStatus coreaudio_get_voice(AudioDeviceID *id)
+static OSStatus coreaudio_get_voice_out(AudioDeviceID *id)
{
UInt32 size = sizeof(*id);
return AudioObjectGetPropertyData(kAudioObjectSystemObject,
- &voice_addr,
+ &voice_out_addr,
0,
NULL,
&size,
id);
}
-static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
- AudioValueRange *framerange)
+static OSStatus coreaudio_get_out_framesizerange(AudioDeviceID id,
+ AudioValueRange *framerange)
{
UInt32 size = sizeof(*framerange);
AudioObjectPropertyAddress addr = {
@@ -80,7 +80,7 @@ static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
framerange);
}
-static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
+static OSStatus coreaudio_get_out_framesize(AudioDeviceID id, UInt32 *framesize)
{
UInt32 size = sizeof(*framesize);
AudioObjectPropertyAddress addr = {
@@ -97,7 +97,7 @@ static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
framesize);
}
-static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
+static OSStatus coreaudio_set_out_framesize(AudioDeviceID id, UInt32 *framesize)
{
UInt32 size = sizeof(*framesize);
AudioObjectPropertyAddress addr = {
@@ -114,8 +114,8 @@ static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
framesize);
}
-static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
- AudioStreamBasicDescription *d)
+static OSStatus coreaudio_set_out_streamformat(AudioDeviceID id,
+ AudioStreamBasicDescription *d)
{
UInt32 size = sizeof(*d);
AudioObjectPropertyAddress addr = {
@@ -132,7 +132,7 @@ static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
d);
}
-static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
+static OSStatus coreaudio_get_out_isrunning(AudioDeviceID id, UInt32 *result)
{
UInt32 size = sizeof(*result);
AudioObjectPropertyAddress addr = {
@@ -149,7 +149,7 @@ static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
result);
}
-static void coreaudio_logstatus (OSStatus status)
+static void coreaudio_logstatus(OSStatus status)
{
const char *str = "BUG";
@@ -199,14 +199,14 @@ static void coreaudio_logstatus (OSStatus status)
break;
default:
- AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status);
+ AUD_log(AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status);
return;
}
- AUD_log (AUDIO_CAP, "Reason: %s\n", str);
+ AUD_log(AUDIO_CAP, "Reason: %s\n", str);
}
-static void G_GNUC_PRINTF (2, 3) coreaudio_logerr (
+static void G_GNUC_PRINTF(2, 3) coreaudio_logerr(
OSStatus status,
const char *fmt,
...
@@ -214,14 +214,14 @@ static void G_GNUC_PRINTF (2, 3) coreaudio_logerr (
{
va_list ap;
- va_start (ap, fmt);
- AUD_log (AUDIO_CAP, fmt, ap);
- va_end (ap);
+ va_start(ap, fmt);
+ AUD_log(AUDIO_CAP, fmt, ap);
+ va_end(ap);
- coreaudio_logstatus (status);
+ coreaudio_logstatus(status);
}
-static void G_GNUC_PRINTF (3, 4) coreaudio_logerr2 (
+static void G_GNUC_PRINTF(3, 4) coreaudio_logerr2(
OSStatus status,
const char *typ,
const char *fmt,
@@ -230,58 +230,60 @@ static void G_GNUC_PRINTF (3, 4) coreaudio_logerr2 (
{
va_list ap;
- AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
+ AUD_log(AUDIO_CAP, "Could not initialize %s\n", typ);
- va_start (ap, fmt);
- AUD_vlog (AUDIO_CAP, fmt, ap);
- va_end (ap);
+ va_start(ap, fmt);
+ AUD_vlog(AUDIO_CAP, fmt, ap);
+ va_end(ap);
- coreaudio_logstatus (status);
+ coreaudio_logstatus(status);
}
#define coreaudio_playback_logerr(status, ...) \
coreaudio_logerr2(status, "playback", __VA_ARGS__)
-static int coreaudio_buf_lock (coreaudioVoiceOut *core, const char *fn_name)
+static int coreaudio_voice_out_buf_lock(CoreaudioVoiceOut *core,
+ const char *fn_name)
{
int err;
- err = pthread_mutex_lock (&core->buf_mutex);
+ err = pthread_mutex_lock(&core->buf_mutex);
if (err) {
- dolog ("Could not lock voice for %s\nReason: %s\n",
- fn_name, strerror (err));
+ dolog("Could not lock voice for %s\nReason: %s\n",
+ fn_name, strerror(err));
return -1;
}
return 0;
}
-static int coreaudio_buf_unlock (coreaudioVoiceOut *core, const char *fn_name)
+static int coreaudio_voice_out_buf_unlock(CoreaudioVoiceOut *core,
+ const char *fn_name)
{
int err;
- err = pthread_mutex_unlock (&core->buf_mutex);
+ err = pthread_mutex_unlock(&core->buf_mutex);
if (err) {
- dolog ("Could not unlock voice for %s\nReason: %s\n",
- fn_name, strerror (err));
+ dolog("Could not unlock voice for %s\nReason: %s\n",
+ fn_name, strerror(err));
return -1;
}
return 0;
}
-#define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \
- static ret_type glue(coreaudio_, name)args_decl \
- { \
- coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; \
- ret_type ret; \
- \
- if (coreaudio_buf_lock(core, "coreaudio_" #name)) { \
- return 0; \
- } \
- \
- ret = glue(audio_generic_, name)args; \
- \
- coreaudio_buf_unlock(core, "coreaudio_" #name); \
- return ret; \
+#define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \
+ static ret_type glue(coreaudio_, name)args_decl \
+ { \
+ CoreaudioVoiceOut *core = (CoreaudioVoiceOut *)hw; \
+ ret_type ret; \
+ \
+ if (coreaudio_voice_out_buf_lock(core, "coreaudio_" #name)) { \
+ return 0; \
+ } \
+ \
+ ret = glue(audio_generic_, name)args; \
+ \
+ coreaudio_voice_out_buf_unlock(core, "coreaudio_" #name); \
+ return ret; \
}
COREAUDIO_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw))
COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
@@ -297,7 +299,7 @@ static ret_type glue(coreaudio_, name)args_decl \
* callback to feed audiooutput buffer. called without BQL.
* allowed to lock "buf_mutex", but disallowed to have any other locks.
*/
-static OSStatus audioDeviceIOProc(
+static OSStatus out_device_ioproc(
AudioDeviceID inDevice,
const AudioTimeStamp *inNow,
const AudioBufferList *inInputData,
@@ -306,33 +308,33 @@ static OSStatus audioDeviceIOProc(
const AudioTimeStamp *inOutputTime,
void *hwptr)
{
- UInt32 frameCount, pending_frames;
+ UInt32 frame_size, pending_frames;
void *out = outOutputData->mBuffers[0].mData;
HWVoiceOut *hw = hwptr;
- coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
+ CoreaudioVoiceOut *core = hwptr;
size_t len;
- if (coreaudio_buf_lock (core, "audioDeviceIOProc")) {
+ if (coreaudio_voice_out_buf_lock(core, "out_device_ioproc")) {
inInputTime = 0;
return 0;
}
- if (inDevice != core->outputDeviceID) {
- coreaudio_buf_unlock (core, "audioDeviceIOProc(old device)");
+ if (inDevice != core->device_id) {
+ coreaudio_voice_out_buf_unlock(core, "out_device_ioproc(old device)");
return 0;
}
- frameCount = core->audioDevicePropertyBufferFrameSize;
+ frame_size = core->device_frame_size;
pending_frames = hw->pending_emul / hw->info.bytes_per_frame;
/* if there are not enough samples, set signal and return */
- if (pending_frames < frameCount) {
+ if (pending_frames < frame_size) {
inInputTime = 0;
- coreaudio_buf_unlock (core, "audioDeviceIOProc(empty)");
+ coreaudio_voice_out_buf_unlock(core, "out_device_ioproc(empty)");
return 0;
}
- len = frameCount * hw->info.bytes_per_frame;
+ len = frame_size * hw->info.bytes_per_frame;
while (len) {
size_t write_len, start;
@@ -348,16 +350,19 @@ static OSStatus audioDeviceIOProc(
out += write_len;
}
- coreaudio_buf_unlock (core, "audioDeviceIOProc");
+ coreaudio_voice_out_buf_unlock(core, "out_device_ioproc");
return 0;
}
-static OSStatus init_out_device(coreaudioVoiceOut *core)
+static OSStatus init_out_device(CoreaudioVoiceOut *core)
{
OSStatus status;
- AudioValueRange frameRange;
+ AudioDeviceID device_id;
+ AudioValueRange framerange;
+ UInt32 device_frame_size;
+ AudioDeviceIOProcID ioprocid;
- AudioStreamBasicDescription streamBasicDescription = {
+ AudioStreamBasicDescription stream_basic_description = {
.mBitsPerChannel = core->hw.info.bits,
.mBytesPerFrame = core->hw.info.bytes_per_frame,
.mBytesPerPacket = core->hw.info.bytes_per_frame,
@@ -368,76 +373,71 @@ static OSStatus init_out_device(coreaudioVoiceOut *core)
.mSampleRate = core->hw.info.freq
};
- status = coreaudio_get_voice(&core->outputDeviceID);
+ status = coreaudio_get_voice_out(&device_id);
if (status != kAudioHardwareNoError) {
- coreaudio_playback_logerr (status,
- "Could not get default output Device\n");
+ coreaudio_playback_logerr(status,
+ "Could not get default output Device\n");
return status;
}
- if (core->outputDeviceID == kAudioDeviceUnknown) {
- dolog ("Could not initialize playback - Unknown Audiodevice\n");
+ if (device_id == kAudioDeviceUnknown) {
+ dolog("Could not initialize playback - Unknown Audiodevice\n");
return status;
}
/* get minimum and maximum buffer frame sizes */
- status = coreaudio_get_framesizerange(core->outputDeviceID,
- &frameRange);
+ status = coreaudio_get_out_framesizerange(device_id, &framerange);
if (status == kAudioHardwareBadObjectError) {
return 0;
}
if (status != kAudioHardwareNoError) {
- coreaudio_playback_logerr (status,
- "Could not get device buffer frame range\n");
+ coreaudio_playback_logerr(status,
+ "Could not get device buffer frame range\n");
return status;
}
- if (frameRange.mMinimum > core->frameSizeSetting) {
- core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
- dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
- } else if (frameRange.mMaximum < core->frameSizeSetting) {
- core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
- dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
+ if (framerange.mMinimum > core->frame_size_setting) {
+ device_frame_size = framerange.mMinimum;
+ dolog("warning: Upsizing Buffer Frames to %f\n", framerange.mMinimum);
+ } else if (framerange.mMaximum < core->frame_size_setting) {
+ device_frame_size = framerange.mMaximum;
+ dolog("warning: Downsizing Buffer Frames to %f\n", framerange.mMaximum);
} else {
- core->audioDevicePropertyBufferFrameSize = core->frameSizeSetting;
+ device_frame_size = core->frame_size_setting;
}
/* set Buffer Frame Size */
- status = coreaudio_set_framesize(core->outputDeviceID,
- &core->audioDevicePropertyBufferFrameSize);
+ status = coreaudio_set_out_framesize(device_id, &device_frame_size);
if (status == kAudioHardwareBadObjectError) {
return 0;
}
if (status != kAudioHardwareNoError) {
- coreaudio_playback_logerr (status,
- "Could not set device buffer frame size %" PRIu32 "\n",
- (uint32_t)core->audioDevicePropertyBufferFrameSize);
+ coreaudio_playback_logerr(status,
+ "Could not set device buffer frame size %" PRIu32 "\n",
+ (uint32_t)device_frame_size);
return status;
}
/* get Buffer Frame Size */
- status = coreaudio_get_framesize(core->outputDeviceID,
- &core->audioDevicePropertyBufferFrameSize);
+ status = coreaudio_get_out_framesize(device_id, &device_frame_size);
if (status == kAudioHardwareBadObjectError) {
return 0;
}
if (status != kAudioHardwareNoError) {
- coreaudio_playback_logerr (status,
- "Could not get device buffer frame size\n");
+ coreaudio_playback_logerr(status,
+ "Could not get device buffer frame size\n");
return status;
}
- core->hw.samples = core->bufferCount * core->audioDevicePropertyBufferFrameSize;
/* set Samplerate */
- status = coreaudio_set_streamformat(core->outputDeviceID,
- &streamBasicDescription);
+ status = coreaudio_set_out_streamformat(device_id,
+ &stream_basic_description);
if (status == kAudioHardwareBadObjectError) {
return 0;
}
if (status != kAudioHardwareNoError) {
- coreaudio_playback_logerr (status,
- "Could not set samplerate %lf\n",
- streamBasicDescription.mSampleRate);
- core->outputDeviceID = kAudioDeviceUnknown;
+ coreaudio_playback_logerr(status,
+ "Could not set samplerate %lf\n",
+ stream_basic_description.mSampleRate);
return status;
}
@@ -451,30 +451,35 @@ static OSStatus init_out_device(coreaudioVoiceOut *core)
* Therefore, the specified callback must be designed to avoid a deadlock
* with the callers of AudioObjectGetPropertyData.
*/
- core->ioprocid = NULL;
- status = AudioDeviceCreateIOProcID(core->outputDeviceID,
- audioDeviceIOProc,
+ ioprocid = NULL;
+ status = AudioDeviceCreateIOProcID(device_id,
+ out_device_ioproc,
&core->hw,
- &core->ioprocid);
+ &ioprocid);
if (status == kAudioHardwareBadDeviceError) {
return 0;
}
- if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
- coreaudio_playback_logerr (status, "Could not set IOProc\n");
- core->outputDeviceID = kAudioDeviceUnknown;
+ if (status != kAudioHardwareNoError || ioprocid == NULL) {
+ coreaudio_playback_logerr(status, "Could not set IOProc\n");
return status;
}
+ core->device_id = device_id;
+ core->device_frame_size = device_frame_size;
+ core->hw.samples = core->buffer_count * core->device_frame_size;
+ audio_generic_initialize_buffer_out(&core->hw);
+ core->ioprocid = ioprocid;
+
return 0;
}
-static void fini_out_device(coreaudioVoiceOut *core)
+static void fini_out_device(CoreaudioVoiceOut *core)
{
OSStatus status;
UInt32 isrunning;
/* stop playback */
- status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
+ status = coreaudio_get_out_isrunning(core->device_id, &isrunning);
if (status != kAudioHardwareBadObjectError) {
if (status != kAudioHardwareNoError) {
coreaudio_logerr(status,
@@ -482,7 +487,7 @@ static void fini_out_device(coreaudioVoiceOut *core)
}
if (isrunning) {
- status = AudioDeviceStop(core->outputDeviceID, core->ioprocid);
+ status = AudioDeviceStop(core->device_id, core->ioprocid);
if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
coreaudio_logerr(status, "Could not stop playback\n");
}
@@ -490,20 +495,20 @@ static void fini_out_device(coreaudioVoiceOut *core)
}
/* remove callback */
- status = AudioDeviceDestroyIOProcID(core->outputDeviceID,
+ status = AudioDeviceDestroyIOProcID(core->device_id,
core->ioprocid);
if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
coreaudio_logerr(status, "Could not remove IOProc\n");
}
- core->outputDeviceID = kAudioDeviceUnknown;
+ core->device_id = kAudioDeviceUnknown;
}
-static void update_device_playback_state(coreaudioVoiceOut *core)
+static void update_out_device_playback_state(CoreaudioVoiceOut *core)
{
OSStatus status;
UInt32 isrunning;
- status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
+ status = coreaudio_get_out_isrunning(core->device_id, &isrunning);
if (status != kAudioHardwareNoError) {
if (status != kAudioHardwareBadObjectError) {
coreaudio_logerr(status,
@@ -516,15 +521,15 @@ static void update_device_playback_state(coreaudioVoiceOut *core)
if (core->enabled) {
/* start playback */
if (!isrunning) {
- status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
+ status = AudioDeviceStart(core->device_id, core->ioprocid);
if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
- coreaudio_logerr (status, "Could not resume playback\n");
+ coreaudio_logerr(status, "Could not resume playback\n");
}
}
} else {
/* stop playback */
if (isrunning) {
- status = AudioDeviceStop(core->outputDeviceID,
+ status = AudioDeviceStop(core->device_id,
core->ioprocid);
if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
coreaudio_logerr(status, "Could not pause playback\n");
@@ -534,22 +539,24 @@ static void update_device_playback_state(coreaudioVoiceOut *core)
}
/* called without BQL. */
-static OSStatus handle_voice_change(
+static OSStatus handle_voice_out_change(
AudioObjectID in_object_id,
UInt32 in_number_addresses,
const AudioObjectPropertyAddress *in_addresses,
void *in_client_data)
{
- coreaudioVoiceOut *core = in_client_data;
+ CoreaudioVoiceOut *core = in_client_data;
bql_lock();
- if (core->outputDeviceID) {
+ if (core->device_id) {
fini_out_device(core);
}
- if (!init_out_device(core)) {
- update_device_playback_state(core);
+ init_out_device(core);
+
+ if (core->device_id) {
+ update_out_device_playback_state(core);
}
bql_unlock();
@@ -560,7 +567,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
void *drv_opaque)
{
OSStatus status;
- coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
+ CoreaudioVoiceOut *core = (CoreaudioVoiceOut *)hw;
int err;
Audiodev *dev = drv_opaque;
AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
@@ -569,33 +576,34 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
/* create mutex */
err = pthread_mutex_init(&core->buf_mutex, NULL);
if (err) {
- dolog("Could not create mutex\nReason: %s\n", strerror (err));
+ dolog("Could not create mutex\nReason: %s\n", strerror(err));
return -1;
}
obt_as = *as;
as = &obt_as;
as->fmt = AUDIO_FORMAT_F32;
- audio_pcm_init_info (&hw->info, as);
+ audio_pcm_init_info(&hw->info, as);
- core->frameSizeSetting = audio_buffer_frames(
+ core->frame_size_setting = audio_buffer_frames(
qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
- core->bufferCount = cpdo->has_buffer_count ? cpdo->buffer_count : 4;
+ core->buffer_count = cpdo->has_buffer_count ? cpdo->buffer_count : 4;
status = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
- &voice_addr, handle_voice_change,
+ &voice_out_addr,
+ handle_voice_out_change,
core);
if (status != kAudioHardwareNoError) {
- coreaudio_playback_logerr (status,
- "Could not listen to voice property change\n");
+ coreaudio_playback_logerr(status,
+ "Could not listen to voice property change\n");
return -1;
}
if (init_out_device(core)) {
status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
- &voice_addr,
- handle_voice_change,
+ &voice_out_addr,
+ handle_voice_out_change,
core);
if (status != kAudioHardwareNoError) {
coreaudio_playback_logerr(status,
@@ -612,11 +620,11 @@ static void coreaudio_fini_out (HWVoiceOut *hw)
{
OSStatus status;
int err;
- coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
+ CoreaudioVoiceOut *core = (CoreaudioVoiceOut *)hw;
status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
- &voice_addr,
- handle_voice_change,
+ &voice_out_addr,
+ handle_voice_out_change,
core);
if (status != kAudioHardwareNoError) {
coreaudio_logerr(status, "Could not remove voice property change listener\n");
@@ -627,16 +635,16 @@ static void coreaudio_fini_out (HWVoiceOut *hw)
/* destroy mutex */
err = pthread_mutex_destroy(&core->buf_mutex);
if (err) {
- dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
+ dolog("Could not destroy mutex\nReason: %s\n", strerror(err));
}
}
static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
{
- coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
+ CoreaudioVoiceOut *core = (CoreaudioVoiceOut *)hw;
core->enabled = enable;
- update_device_playback_state(core);
+ update_out_device_playback_state(core);
}
static void *coreaudio_audio_init(Audiodev *dev, Error **errp)
@@ -644,7 +652,7 @@ static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
return dev;
}
-static void coreaudio_audio_fini (void *opaque)
+static void coreaudio_audio_fini(void *opaque)
{
}
@@ -670,7 +678,7 @@ static void coreaudio_audio_fini (void *opaque)
.pcm_ops = &coreaudio_pcm_ops,
.max_voices_out = 1,
.max_voices_in = 0,
- .voice_size_out = sizeof (coreaudioVoiceOut),
+ .voice_size_out = sizeof(CoreaudioVoiceOut),
.voice_size_in = 0
};
diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c
index 94ddc01..23e6c41 100644
--- a/hw/display/virtio-gpu-virgl.c
+++ b/hw/display/virtio-gpu-virgl.c
@@ -396,11 +396,42 @@ static void virgl_cmd_resource_flush(VirtIOGPU *g,
}
}
+static DisplayGLTexture virgl_borrow_texture_for_scanout(uint32_t id)
+{
+ DisplayGLTexture texture = {};
+ struct virgl_renderer_resource_info info;
+ int ret;
+
+#if VIRGL_VERSION_MAJOR >= 1
+ struct virgl_renderer_resource_info_ext ext;
+ memset(&ext, 0, sizeof(ext));
+#ifdef HAVE_VIRGL_RENDERER_BORROW_TEXTURE_FOR_SCANOUT
+ ret = virgl_renderer_borrow_texture_for_scanout(id, &ext);
+#else
+ ret = virgl_renderer_resource_get_info_ext(id, &ext);
+#endif
+ info = ext.base;
+ texture.d3d_tex2d = ext.d3d_tex2d;
+#else
+ memset(&info, 0, sizeof(info));
+ ret = virgl_renderer_resource_get_info(id, &info);
+#endif
+ if (ret) {
+ return texture;
+ }
+
+ texture.y_0_top = info.flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP;
+ texture.width = info.width;
+ texture.height = info.height;
+ texture.id = info.tex_id;
+
+ return texture;
+}
+
static void virgl_cmd_set_scanout(VirtIOGPU *g,
struct virtio_gpu_ctrl_command *cmd)
{
struct virtio_gpu_set_scanout ss;
- int ret;
VIRTIO_GPU_FILL_CMD(ss);
trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id,
@@ -415,35 +446,13 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g,
g->parent_obj.enable = 1;
if (ss.resource_id && ss.r.width && ss.r.height) {
- struct virgl_renderer_resource_info info;
- void *d3d_tex2d = NULL;
-
-#if VIRGL_VERSION_MAJOR >= 1
- struct virgl_renderer_resource_info_ext ext;
- memset(&ext, 0, sizeof(ext));
- ret = virgl_renderer_resource_get_info_ext(ss.resource_id, &ext);
- info = ext.base;
- d3d_tex2d = ext.d3d_tex2d;
-#else
- memset(&info, 0, sizeof(info));
- ret = virgl_renderer_resource_get_info(ss.resource_id, &info);
-#endif
- if (ret) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: illegal resource specified %d\n",
- __func__, ss.resource_id);
- cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
- return;
- }
qemu_console_resize(g->parent_obj.scanout[ss.scanout_id].con,
ss.r.width, ss.r.height);
virgl_renderer_force_ctx_0();
dpy_gl_scanout_texture(
- g->parent_obj.scanout[ss.scanout_id].con, info.tex_id,
- info.flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP,
- info.width, info.height,
- ss.r.x, ss.r.y, ss.r.width, ss.r.height,
- d3d_tex2d);
+ g->parent_obj.scanout[ss.scanout_id].con, ss.resource_id,
+ virgl_borrow_texture_for_scanout,
+ ss.r.x, ss.r.y, ss.r.width, ss.r.height);
} else {
dpy_gfx_replace_surface(
g->parent_obj.scanout[ss.scanout_id].con, NULL);
diff --git a/include/ui/console.h b/include/ui/console.h
index 98feaa5..bdfc0ac 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -131,16 +131,23 @@ struct QemuConsoleClass {
ObjectClass parent_class;
};
+typedef struct DisplayGLTexture {
+ uint32_t id;
+ bool y_0_top;
+ uint32_t width;
+ uint32_t height;
+ void *d3d_tex2d;
+} DisplayGLTexture;
+
+typedef DisplayGLTexture (*DisplayGLTextureBorrower)(uint32_t id);
+
typedef struct ScanoutTexture {
uint32_t backing_id;
- bool backing_y_0_top;
- uint32_t backing_width;
- uint32_t backing_height;
+ DisplayGLTextureBorrower backing_borrow;
uint32_t x;
uint32_t y;
uint32_t width;
uint32_t height;
- void *d3d_tex2d;
} ScanoutTexture;
typedef struct QemuUIInfo {
@@ -240,12 +247,9 @@ typedef struct DisplayChangeListenerOps {
/* required if GL */
void (*dpy_gl_scanout_texture)(DisplayChangeListener *dcl,
uint32_t backing_id,
- bool backing_y_0_top,
- uint32_t backing_width,
- uint32_t backing_height,
+ DisplayGLTextureBorrower backing_borrow,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h,
- void *d3d_tex2d);
+ uint32_t w, uint32_t h);
/* optional (default to true if has dpy_gl_scanout_dmabuf) */
bool (*dpy_has_dmabuf)(DisplayChangeListener *dcl);
/* optional */
@@ -325,11 +329,9 @@ bool dpy_gfx_check_format(QemuConsole *con,
pixman_format_code_t format);
void dpy_gl_scanout_disable(QemuConsole *con);
-void dpy_gl_scanout_texture(QemuConsole *con,
- uint32_t backing_id, bool backing_y_0_top,
- uint32_t backing_width, uint32_t backing_height,
- uint32_t x, uint32_t y, uint32_t w, uint32_t h,
- void *d3d_tex2d);
+void dpy_gl_scanout_texture(QemuConsole *con, uint32_t backing_id,
+ DisplayGLTextureBorrower backing_borrow,
+ uint32_t x, uint32_t y, uint32_t w, uint32_t h);
void dpy_gl_scanout_dmabuf(QemuConsole *con,
QemuDmaBuf *dmabuf);
void dpy_gl_cursor_dmabuf(QemuConsole *con, QemuDmaBuf *dmabuf,
diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h
index acf993f..63eed82 100644
--- a/include/ui/egl-helpers.h
+++ b/include/ui/egl-helpers.h
@@ -59,7 +59,9 @@ void egl_dmabuf_create_fence(QemuDmaBuf *dmabuf);
#endif
-EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, EGLNativeWindowType win);
+EGLSurface qemu_egl_init_surface(EGLContext ectx, EGLNativeWindowType win);
+
+int qemu_egl_init_dpy_cocoa(DisplayGLMode mode);
#if defined(CONFIG_X11) || defined(CONFIG_GBM)
diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index 3e6ce3c..e9be300 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -21,7 +21,7 @@
#include "ui/clipboard.h"
#include "ui/console.h"
#include "ui/kbd-state.h"
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
#include "ui/egl-helpers.h"
#include "ui/egl-context.h"
#endif
@@ -44,7 +44,7 @@ typedef struct VirtualGfxConsole {
double preferred_scale;
double scale_x;
double scale_y;
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
QemuGLShader *gls;
EGLContext ectx;
EGLSurface esurface;
@@ -173,12 +173,9 @@ QEMUGLContext gd_egl_create_context(DisplayGLCtx *dgc,
void gd_egl_scanout_disable(DisplayChangeListener *dcl);
void gd_egl_scanout_texture(DisplayChangeListener *dcl,
uint32_t backing_id,
- bool backing_y_0_top,
- uint32_t backing_width,
- uint32_t backing_height,
+ DisplayGLTextureBorrower backing_borrow,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h,
- void *d3d_tex2d);
+ uint32_t w, uint32_t h);
void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf);
void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl,
@@ -210,12 +207,9 @@ void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf);
void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
uint32_t backing_id,
- bool backing_y_0_top,
- uint32_t backing_width,
- uint32_t backing_height,
+ DisplayGLTextureBorrower backing_borrow,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h,
- void *d3d_tex2d);
+ uint32_t w, uint32_t h);
void gd_gl_area_scanout_disable(DisplayChangeListener *dcl);
void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
uint32_t x, uint32_t y, uint32_t w, uint32_t h);
diff --git a/include/ui/sdl2.h b/include/ui/sdl2.h
index dbe6e3d..6afa2f5 100644
--- a/include/ui/sdl2.h
+++ b/include/ui/sdl2.h
@@ -22,7 +22,7 @@
#endif
#include "ui/kbd-state.h"
-#ifdef CONFIG_OPENGL
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
# include "ui/egl-helpers.h"
#endif
@@ -45,7 +45,7 @@ struct sdl2_console {
bool gui_keysym;
SDL_GLContext winctx;
QKbdState *kbd;
-#ifdef CONFIG_OPENGL
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
QemuGLShader *gls;
egl_fb guest_fb;
egl_fb win_fb;
@@ -88,12 +88,9 @@ int sdl2_gl_make_context_current(DisplayGLCtx *dgc,
void sdl2_gl_scanout_disable(DisplayChangeListener *dcl);
void sdl2_gl_scanout_texture(DisplayChangeListener *dcl,
uint32_t backing_id,
- bool backing_y_0_top,
- uint32_t backing_width,
- uint32_t backing_height,
+ DisplayGLTextureBorrower backing_borrow,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h,
- void *d3d_tex2d);
+ uint32_t w, uint32_t h);
void sdl2_gl_scanout_flush(DisplayChangeListener *dcl,
uint32_t x, uint32_t y, uint32_t w, uint32_t h);
diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h
index 690ece7..02cf113 100644
--- a/include/ui/spice-display.h
+++ b/include/ui/spice-display.h
@@ -27,7 +27,7 @@
#include "ui/qemu-pixman.h"
#include "ui/console.h"
-#if defined(CONFIG_OPENGL) && defined(CONFIG_GBM)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL) && defined(CONFIG_GBM)
# define HAVE_SPICE_GL 1
# include "ui/egl-helpers.h"
# include "ui/egl-context.h"
diff --git a/meson.build b/meson.build
index b7db736..79b9134 100644
--- a/meson.build
+++ b/meson.build
@@ -1210,7 +1210,7 @@ if get_option('attr').allowed()
endif
cocoa = dependency('appleframeworks',
- modules: ['Cocoa', 'CoreVideo', 'QuartzCore'],
+ modules: ['Cocoa', 'CoreVideo', 'OpenGL', 'QuartzCore'],
required: get_option('cocoa'))
vmnet = dependency('appleframeworks', modules: 'vmnet', required: get_option('vmnet'))
@@ -1788,14 +1788,13 @@ if not get_option('coreaudio').auto() or (host_os == 'darwin' and have_system)
required: get_option('coreaudio'))
endif
+egl = not_found
opengl = not_found
if not get_option('opengl').auto() or have_system or have_vhost_user_gpu
- epoxy = dependency('epoxy', method: 'pkg-config',
+ opengl = dependency('epoxy', method: 'pkg-config',
required: get_option('opengl'))
- if cc.has_header('epoxy/egl.h', dependencies: epoxy)
- opengl = epoxy
- elif get_option('opengl').enabled()
- error('epoxy/egl.h not found')
+ if cc.has_header('epoxy/egl.h', dependencies: opengl)
+ egl = opengl
endif
endif
gbm = not_found
@@ -2545,6 +2544,7 @@ if numa.found()
cc.has_function('numa_has_preferred_many',
dependencies: numa))
endif
+config_host_data.set('CONFIG_EGL', egl.found())
config_host_data.set('CONFIG_OPENGL', opengl.found())
config_host_data.set('CONFIG_PLUGIN', get_option('plugins'))
config_host_data.set('CONFIG_RBD', rbd.found())
@@ -2586,6 +2586,10 @@ config_host_data.set('CONFIG_VNC', vnc.found())
config_host_data.set('CONFIG_VNC_JPEG', jpeg.found())
config_host_data.set('CONFIG_VNC_SASL', sasl.found())
if virgl.found()
+ config_host_data.set('HAVE_VIRGL_RENDERER_BORROW_TEXTURE_FOR_SCANOUT',
+ cc.has_function('virgl_renderer_borrow_texture_for_scanout',
+ prefix: '#include <virglrenderer.h>',
+ dependencies: virgl))
config_host_data.set('VIRGL_VERSION_MAJOR', virgl.version().split('.')[0])
endif
config_host_data.set('CONFIG_VIRTFS', have_virtfs)
diff --git a/qapi/ui.json b/qapi/ui.json
index 1b2f4a4..8419f7b 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -1520,7 +1520,8 @@
{ 'name': 'none' },
{ 'name': 'gtk', 'if': 'CONFIG_GTK' },
{ 'name': 'sdl', 'if': 'CONFIG_SDL' },
- { 'name': 'egl-headless', 'if': 'CONFIG_OPENGL' },
+ { 'name': 'egl-headless',
+ 'if': { 'all': ['CONFIG_OPENGL', 'CONFIG_EGL'] } },
{ 'name': 'curses', 'if': 'CONFIG_CURSES' },
{ 'name': 'cocoa', 'if': 'CONFIG_COCOA' },
{ 'name': 'spice-app', 'if': 'CONFIG_SPICE' },
@@ -1560,7 +1561,7 @@
'cocoa': { 'type': 'DisplayCocoa', 'if': 'CONFIG_COCOA' },
'curses': { 'type': 'DisplayCurses', 'if': 'CONFIG_CURSES' },
'egl-headless': { 'type': 'DisplayEGLHeadless',
- 'if': 'CONFIG_OPENGL' },
+ 'if': { 'all': ['CONFIG_OPENGL', 'CONFIG_EGL'] } },
'dbus': { 'type': 'DisplayDBus', 'if': 'CONFIG_DBUS_DISPLAY' },
'sdl': { 'type': 'DisplaySDL', 'if': 'CONFIG_SDL' }
}
diff --git a/qemu-options.hx b/qemu-options.hx
index ab23f14..7e12e42 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2116,7 +2116,7 @@ DEF("display", HAS_ARG, QEMU_OPTION_display,
" [,show-cursor=on|off][,left-command-key=on|off]\n"
" [,full-screen=on|off][,zoom-to-fit=on|off]\n"
#endif
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
"-display egl-headless[,rendernode=<file>]\n"
#endif
#if defined(CONFIG_DBUS_DISPLAY)
diff --git a/ui/cocoa.m b/ui/cocoa.m
index 23b7a73..2317952 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -22,6 +22,8 @@
* THE SOFTWARE.
*/
+#define GL_SILENCE_DEPRECATION
+
#include "qemu/osdep.h"
#import <Cocoa/Cocoa.h>
@@ -51,6 +53,10 @@
#include <Carbon/Carbon.h>
#include "hw/core/cpu.h"
+#ifdef CONFIG_EGL
+#include "ui/egl-context.h"
+#endif
+
#ifndef MAC_OS_VERSION_14_0
#define MAC_OS_VERSION_14_0 140000
#endif
@@ -75,33 +81,14 @@
@class QemuCocoaPasteboardTypeOwner;
-static void cocoa_update(DisplayChangeListener *dcl,
- int x, int y, int w, int h);
-
-static void cocoa_switch(DisplayChangeListener *dcl,
- DisplaySurface *surface);
-
-static void cocoa_refresh(DisplayChangeListener *dcl);
-static void cocoa_mouse_set(DisplayChangeListener *dcl, int x, int y, bool on);
-static void cocoa_cursor_define(DisplayChangeListener *dcl, QEMUCursor *cursor);
-
-static const DisplayChangeListenerOps dcl_ops = {
- .dpy_name = "cocoa",
- .dpy_gfx_update = cocoa_update,
- .dpy_gfx_switch = cocoa_switch,
- .dpy_refresh = cocoa_refresh,
- .dpy_mouse_set = cocoa_mouse_set,
- .dpy_cursor_define = cocoa_cursor_define,
-};
-static DisplayChangeListener dcl = {
- .ops = &dcl_ops,
-};
+static DisplayChangeListener dcl;
+static DisplaySurface *surface;
static QKbdState *kbd;
static int cursor_hide = 1;
static int left_command_key_enabled = 1;
static bool swap_opt_cmd;
-static CGInterpolationQuality zoom_interpolation = kCGInterpolationNone;
+static bool zoom_interpolation;
static NSTextField *pauseLabel;
static bool allow_events;
@@ -111,6 +98,97 @@ static void cocoa_switch(DisplayChangeListener *dcl,
static QemuEvent cbevent;
static QemuCocoaPasteboardTypeOwner *cbowner;
+#ifdef CONFIG_OPENGL
+
+@interface QemuCGLLayer : CAOpenGLLayer
+@end
+
+static bool gl_dirty;
+static uint32_t gl_scanout_id;
+static DisplayGLTextureBorrower gl_scanout_borrow;
+static QEMUGLContext gl_view_ctx;
+
+#ifdef CONFIG_EGL
+static EGLSurface egl_surface;
+#endif
+
+static void cocoa_gl_switch(DisplayChangeListener *dcl,
+ DisplaySurface *new_surface);
+
+static void cocoa_gl_render(void);
+
+static bool cocoa_gl_is_compatible_dcl(DisplayGLCtx *dgc,
+ DisplayChangeListener *dcl);
+
+static QEMUGLContext cocoa_gl_create_context(DisplayGLCtx *dgc,
+ QEMUGLParams *params);
+
+static void cocoa_gl_destroy_context(DisplayGLCtx *dgc, QEMUGLContext ctx);
+
+static int cocoa_gl_make_context_current(DisplayGLCtx *dgc, QEMUGLContext ctx);
+
+static const DisplayGLCtxOps dgc_ops = {
+ .dpy_gl_ctx_is_compatible_dcl = cocoa_gl_is_compatible_dcl,
+ .dpy_gl_ctx_create = cocoa_gl_create_context,
+ .dpy_gl_ctx_destroy = cocoa_gl_destroy_context,
+ .dpy_gl_ctx_make_current = cocoa_gl_make_context_current,
+};
+
+static DisplayGLCtx dgc = {
+ .ops = &dgc_ops,
+};
+
+@implementation QemuCGLLayer
+- (id)init
+{
+ self = [super init];
+ if (self) {
+ [self setAsynchronous:NO];
+ }
+ return self;
+}
+
+- (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pf
+{
+ CGLContextObj ctx;
+ CGLCreateContext(pf, gl_view_ctx, &ctx);
+ return ctx;
+}
+
+- (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask
+{
+ CGLPixelFormatObj pix;
+ GLint npix;
+ CGLPixelFormatAttribute attribs[] = {
+ kCGLPFADisplayMask,
+ mask,
+ kCGLPFAOpenGLProfile,
+ (CGLPixelFormatAttribute)kCGLOGLPVersion_GL4_Core,
+ 0
+ };
+
+ CGLChoosePixelFormat(attribs, &pix, &npix);
+
+ return pix;
+}
+
+- (void)drawInCGLContext:(CGLContextObj)ctx
+ pixelFormat:(CGLPixelFormatObj)pf
+ forLayerTime:(CFTimeInterval)t
+ displayTime:(const CVTimeStamp *)ts
+{
+ BQL_LOCK_GUARD();
+ cocoa_gl_render();
+
+ [super drawInCGLContext:ctx
+ pixelFormat:pf
+ forLayerTime:t
+ displayTime:ts];
+}
+@end
+
+#endif
+
// Utility functions to run specified code block with the BQL held
typedef void (^CodeBlock)(void);
typedef bool (^BoolCodeBlock)(void);
@@ -142,138 +220,13 @@ static bool bool_with_bql(BoolCodeBlock block)
return val;
}
-// Mac to QKeyCode conversion
-static const int mac_to_qkeycode_map[] = {
- [kVK_ANSI_A] = Q_KEY_CODE_A,
- [kVK_ANSI_B] = Q_KEY_CODE_B,
- [kVK_ANSI_C] = Q_KEY_CODE_C,
- [kVK_ANSI_D] = Q_KEY_CODE_D,
- [kVK_ANSI_E] = Q_KEY_CODE_E,
- [kVK_ANSI_F] = Q_KEY_CODE_F,
- [kVK_ANSI_G] = Q_KEY_CODE_G,
- [kVK_ANSI_H] = Q_KEY_CODE_H,
- [kVK_ANSI_I] = Q_KEY_CODE_I,
- [kVK_ANSI_J] = Q_KEY_CODE_J,
- [kVK_ANSI_K] = Q_KEY_CODE_K,
- [kVK_ANSI_L] = Q_KEY_CODE_L,
- [kVK_ANSI_M] = Q_KEY_CODE_M,
- [kVK_ANSI_N] = Q_KEY_CODE_N,
- [kVK_ANSI_O] = Q_KEY_CODE_O,
- [kVK_ANSI_P] = Q_KEY_CODE_P,
- [kVK_ANSI_Q] = Q_KEY_CODE_Q,
- [kVK_ANSI_R] = Q_KEY_CODE_R,
- [kVK_ANSI_S] = Q_KEY_CODE_S,
- [kVK_ANSI_T] = Q_KEY_CODE_T,
- [kVK_ANSI_U] = Q_KEY_CODE_U,
- [kVK_ANSI_V] = Q_KEY_CODE_V,
- [kVK_ANSI_W] = Q_KEY_CODE_W,
- [kVK_ANSI_X] = Q_KEY_CODE_X,
- [kVK_ANSI_Y] = Q_KEY_CODE_Y,
- [kVK_ANSI_Z] = Q_KEY_CODE_Z,
-
- [kVK_ANSI_0] = Q_KEY_CODE_0,
- [kVK_ANSI_1] = Q_KEY_CODE_1,
- [kVK_ANSI_2] = Q_KEY_CODE_2,
- [kVK_ANSI_3] = Q_KEY_CODE_3,
- [kVK_ANSI_4] = Q_KEY_CODE_4,
- [kVK_ANSI_5] = Q_KEY_CODE_5,
- [kVK_ANSI_6] = Q_KEY_CODE_6,
- [kVK_ANSI_7] = Q_KEY_CODE_7,
- [kVK_ANSI_8] = Q_KEY_CODE_8,
- [kVK_ANSI_9] = Q_KEY_CODE_9,
-
- [kVK_ANSI_Grave] = Q_KEY_CODE_GRAVE_ACCENT,
- [kVK_ANSI_Minus] = Q_KEY_CODE_MINUS,
- [kVK_ANSI_Equal] = Q_KEY_CODE_EQUAL,
- [kVK_Delete] = Q_KEY_CODE_BACKSPACE,
- [kVK_CapsLock] = Q_KEY_CODE_CAPS_LOCK,
- [kVK_Tab] = Q_KEY_CODE_TAB,
- [kVK_Return] = Q_KEY_CODE_RET,
- [kVK_ANSI_LeftBracket] = Q_KEY_CODE_BRACKET_LEFT,
- [kVK_ANSI_RightBracket] = Q_KEY_CODE_BRACKET_RIGHT,
- [kVK_ANSI_Backslash] = Q_KEY_CODE_BACKSLASH,
- [kVK_ANSI_Semicolon] = Q_KEY_CODE_SEMICOLON,
- [kVK_ANSI_Quote] = Q_KEY_CODE_APOSTROPHE,
- [kVK_ANSI_Comma] = Q_KEY_CODE_COMMA,
- [kVK_ANSI_Period] = Q_KEY_CODE_DOT,
- [kVK_ANSI_Slash] = Q_KEY_CODE_SLASH,
- [kVK_Space] = Q_KEY_CODE_SPC,
-
- [kVK_ANSI_Keypad0] = Q_KEY_CODE_KP_0,
- [kVK_ANSI_Keypad1] = Q_KEY_CODE_KP_1,
- [kVK_ANSI_Keypad2] = Q_KEY_CODE_KP_2,
- [kVK_ANSI_Keypad3] = Q_KEY_CODE_KP_3,
- [kVK_ANSI_Keypad4] = Q_KEY_CODE_KP_4,
- [kVK_ANSI_Keypad5] = Q_KEY_CODE_KP_5,
- [kVK_ANSI_Keypad6] = Q_KEY_CODE_KP_6,
- [kVK_ANSI_Keypad7] = Q_KEY_CODE_KP_7,
- [kVK_ANSI_Keypad8] = Q_KEY_CODE_KP_8,
- [kVK_ANSI_Keypad9] = Q_KEY_CODE_KP_9,
- [kVK_ANSI_KeypadDecimal] = Q_KEY_CODE_KP_DECIMAL,
- [kVK_ANSI_KeypadEnter] = Q_KEY_CODE_KP_ENTER,
- [kVK_ANSI_KeypadPlus] = Q_KEY_CODE_KP_ADD,
- [kVK_ANSI_KeypadMinus] = Q_KEY_CODE_KP_SUBTRACT,
- [kVK_ANSI_KeypadMultiply] = Q_KEY_CODE_KP_MULTIPLY,
- [kVK_ANSI_KeypadDivide] = Q_KEY_CODE_KP_DIVIDE,
- [kVK_ANSI_KeypadEquals] = Q_KEY_CODE_KP_EQUALS,
- [kVK_ANSI_KeypadClear] = Q_KEY_CODE_NUM_LOCK,
-
- [kVK_UpArrow] = Q_KEY_CODE_UP,
- [kVK_DownArrow] = Q_KEY_CODE_DOWN,
- [kVK_LeftArrow] = Q_KEY_CODE_LEFT,
- [kVK_RightArrow] = Q_KEY_CODE_RIGHT,
-
- [kVK_Help] = Q_KEY_CODE_INSERT,
- [kVK_Home] = Q_KEY_CODE_HOME,
- [kVK_PageUp] = Q_KEY_CODE_PGUP,
- [kVK_PageDown] = Q_KEY_CODE_PGDN,
- [kVK_End] = Q_KEY_CODE_END,
- [kVK_ForwardDelete] = Q_KEY_CODE_DELETE,
-
- [kVK_Escape] = Q_KEY_CODE_ESC,
-
- /* The Power key can't be used directly because the operating system uses
- * it. This key can be emulated by using it in place of another key such as
- * F1. Don't forget to disable the real key binding.
- */
- /* [kVK_F1] = Q_KEY_CODE_POWER, */
-
- [kVK_F1] = Q_KEY_CODE_F1,
- [kVK_F2] = Q_KEY_CODE_F2,
- [kVK_F3] = Q_KEY_CODE_F3,
- [kVK_F4] = Q_KEY_CODE_F4,
- [kVK_F5] = Q_KEY_CODE_F5,
- [kVK_F6] = Q_KEY_CODE_F6,
- [kVK_F7] = Q_KEY_CODE_F7,
- [kVK_F8] = Q_KEY_CODE_F8,
- [kVK_F9] = Q_KEY_CODE_F9,
- [kVK_F10] = Q_KEY_CODE_F10,
- [kVK_F11] = Q_KEY_CODE_F11,
- [kVK_F12] = Q_KEY_CODE_F12,
- [kVK_F13] = Q_KEY_CODE_PRINT,
- [kVK_F14] = Q_KEY_CODE_SCROLL_LOCK,
- [kVK_F15] = Q_KEY_CODE_PAUSE,
-
- // JIS keyboards only
- [kVK_JIS_Yen] = Q_KEY_CODE_YEN,
- [kVK_JIS_Underscore] = Q_KEY_CODE_RO,
- [kVK_JIS_KeypadComma] = Q_KEY_CODE_KP_COMMA,
- [kVK_JIS_Eisu] = Q_KEY_CODE_MUHENKAN,
- [kVK_JIS_Kana] = Q_KEY_CODE_HENKAN,
-
- /*
- * The eject and volume keys can't be used here because they are handled at
- * a lower level than what an Application can see.
- */
-};
-
static int cocoa_keycode_to_qemu(int keycode)
{
- if (ARRAY_SIZE(mac_to_qkeycode_map) <= keycode) {
+ if (qemu_input_map_osx_to_qcode_len <= keycode) {
error_report("(cocoa) warning unknown keycode 0x%x", keycode);
return 0;
}
- return mac_to_qkeycode_map[keycode];
+ return qemu_input_map_osx_to_qcode[keycode];
}
/* Displays an alert dialog box with the specified message */
@@ -303,7 +256,6 @@ static void handleAnyDeviceErrors(Error * err)
@interface QemuCocoaView : NSView
{
QEMUScreen screen;
- pixman_image_t *pixman_image;
/* The state surrounding mouse grabbing is potentially confusing.
* isAbsoluteEnabled tracks qemu_input_is_absolute() [ie "is the emulated
* pointing device an absolute-position one?"], but is only updated on
@@ -322,7 +274,6 @@ @interface QemuCocoaView : NSView
int mouseY;
bool mouseOn;
}
-- (void) switchSurface:(pixman_image_t *)image;
- (void) grabMouse;
- (void) ungrabMouse;
- (void) setFullGrab:(id)sender;
@@ -331,7 +282,6 @@ - (bool) handleEvent:(NSEvent *)event;
- (bool) handleEventLocked:(NSEvent *)event;
- (void) notifyMouseModeChange;
- (BOOL) isMouseGrabbed;
-- (QEMUScreen) gscreen;
- (void) raiseAllKeys;
@end
@@ -352,6 +302,9 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven
@implementation QemuCocoaView
- (id)initWithFrame:(NSRect)frameRect
+#ifdef CONFIG_OPENGL
+ cgl:(BOOL)cgl
+#endif
{
COCOA_DEBUG("QemuCocoaView: initWithFrame\n");
@@ -378,10 +331,14 @@ - (id)initWithFrame:(NSRect)frameRect
[self setClipsToBounds:YES];
#endif
[self setWantsLayer:YES];
+ if (cgl) {
+ QemuCGLLayer *layer = [[QemuCGLLayer alloc] init];
+ [self setLayer:layer];
+ [layer release];
+ }
cursorLayer = [[CALayer alloc] init];
[cursorLayer setAnchorPoint:CGPointMake(0, 1)];
- [cursorLayer setAutoresizingMask:kCALayerMaxXMargin |
- kCALayerMinYMargin];
+ [cursorLayer setZPosition:1];
[[self layer] addSublayer:cursorLayer];
}
@@ -392,10 +349,6 @@ - (void) dealloc
{
COCOA_DEBUG("QemuCocoaView: dealloc\n");
- if (pixman_image) {
- pixman_image_unref(pixman_image);
- }
-
if (eventsTap) {
CFRelease(eventsTap);
}
@@ -411,6 +364,13 @@ - (BOOL) isOpaque
return YES;
}
+#ifdef CONFIG_OPENGL
+- (BOOL)wantsUpdateLayer
+{
+ return display_opengl;
+}
+#endif
+
- (void) viewDidMoveToWindow
{
[self resizeWindow];
@@ -447,29 +407,43 @@ - (void) unhideCursor
[NSCursor unhide];
}
-- (void)setMouseX:(int)x y:(int)y on:(bool)on
+- (void)updateCursorLayout
{
- CGPoint position;
+ [CATransaction begin];
+ [CATransaction setDisableActions:YES];
- mouseX = x;
- mouseY = y;
- mouseOn = on;
+ if (cursor) {
+ CGFloat scale = [self bounds].size.width / screen.width;
+ CGPoint position;
+ CGRect bounds = CGRectZero;
- position.x = mouseX;
- position.y = screen.height - mouseY;
+ position.x = mouseX * scale;
+ position.y = (screen.height - mouseY) * scale;
+
+ bounds.size.width = cursor->width * scale;
+ bounds.size.height = cursor->height * scale;
+
+ [cursorLayer setBounds:bounds];
+ [cursorLayer setContentsScale:scale];
+ [cursorLayer setPosition:position];
+ }
- [CATransaction begin];
- [CATransaction setDisableActions:YES];
- [cursorLayer setPosition:position];
[cursorLayer setHidden:!mouseOn];
[CATransaction commit];
}
+- (void)setMouseX:(int)x y:(int)y on:(bool)on
+{
+ mouseX = x;
+ mouseY = y;
+ mouseOn = on;
+ [self updateCursorLayout];
+}
+
- (void)setCursor:(QEMUCursor *)given_cursor
{
CGDataProviderRef provider;
CGImageRef image;
- CGRect bounds = CGRectZero;
cursor_unref(cursor);
cursor = given_cursor;
@@ -480,9 +454,6 @@ - (void)setCursor:(QEMUCursor *)given_cursor
cursor_ref(cursor);
- bounds.size.width = cursor->width;
- bounds.size.height = cursor->height;
-
provider = CGDataProviderCreateWithData(
NULL,
cursor->data,
@@ -507,8 +478,8 @@ - (void)setCursor:(QEMUCursor *)given_cursor
CGDataProviderRelease(provider);
[CATransaction begin];
[CATransaction setDisableActions:YES];
- [cursorLayer setBounds:bounds];
[cursorLayer setContents:(id)image];
+ [self updateCursorLayout];
[CATransaction commit];
CGImageRelease(image);
}
@@ -519,61 +490,58 @@ - (void) drawRect:(NSRect) rect
// get CoreGraphic context
CGContextRef viewContextRef = [[NSGraphicsContext currentContext] CGContext];
+ BQL_LOCK_GUARD();
- CGContextSetInterpolationQuality (viewContextRef, zoom_interpolation);
+ CGContextSetInterpolationQuality(viewContextRef,
+ zoom_interpolation ? kCGInterpolationLow :
+ kCGInterpolationNone);
CGContextSetShouldAntialias (viewContextRef, NO);
// draw screen bitmap directly to Core Graphics context
- if (!pixman_image) {
- // Draw request before any guest device has set up a framebuffer:
- // just draw an opaque black rectangle
- CGContextSetRGBFillColor(viewContextRef, 0, 0, 0, 1.0);
- CGContextFillRect(viewContextRef, NSRectToCGRect(rect));
- } else {
- int w = pixman_image_get_width(pixman_image);
- int h = pixman_image_get_height(pixman_image);
- int bitsPerPixel = PIXMAN_FORMAT_BPP(pixman_image_get_format(pixman_image));
- int stride = pixman_image_get_stride(pixman_image);
- CGDataProviderRef dataProviderRef = CGDataProviderCreateWithData(
- NULL,
- pixman_image_get_data(pixman_image),
- stride * h,
- NULL
- );
- CGImageRef imageRef = CGImageCreate(
- w, //width
- h, //height
- DIV_ROUND_UP(bitsPerPixel, 8) * 2, //bitsPerComponent
- bitsPerPixel, //bitsPerPixel
- stride, //bytesPerRow
- colorspace, //colorspace
- kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst, //bitmapInfo
- dataProviderRef, //provider
- NULL, //decode
- 0, //interpolate
- kCGRenderingIntentDefault //intent
- );
- // selective drawing code (draws only dirty rectangles) (OS X >= 10.4)
- const NSRect *rectList;
- NSInteger rectCount;
- int i;
- CGImageRef clipImageRef;
- CGRect clipRect;
-
- [self getRectsBeingDrawn:&rectList count:&rectCount];
- for (i = 0; i < rectCount; i++) {
- clipRect = rectList[i];
- clipRect.origin.y = (float)h - (clipRect.origin.y + clipRect.size.height);
- clipImageRef = CGImageCreateWithImageInRect(
- imageRef,
- clipRect
- );
- CGContextDrawImage (viewContextRef, cgrect(rectList[i]), clipImageRef);
- CGImageRelease (clipImageRef);
- }
- CGImageRelease (imageRef);
- CGDataProviderRelease(dataProviderRef);
- }
+ int w = surface_width(surface);
+ int h = surface_height(surface);
+ int bitsPerPixel = PIXMAN_FORMAT_BPP(surface_format(surface));
+ int stride = surface_stride(surface);
+
+ CGDataProviderRef dataProviderRef = CGDataProviderCreateWithData(
+ NULL,
+ surface_data(surface),
+ stride * h,
+ NULL
+ );
+ CGImageRef imageRef = CGImageCreate(
+ w, //width
+ h, //height
+ DIV_ROUND_UP(bitsPerPixel, 8) * 2, //bitsPerComponent
+ bitsPerPixel, //bitsPerPixel
+ stride, //bytesPerRow
+ colorspace, //colorspace
+ kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst, //bitmapInfo
+ dataProviderRef, //provider
+ NULL, //decode
+ 0, //interpolate
+ kCGRenderingIntentDefault //intent
+ );
+ // selective drawing code (draws only dirty rectangles) (OS X >= 10.4)
+ const NSRect *rectList;
+ NSInteger rectCount;
+ int i;
+ CGImageRef clipImageRef;
+ CGRect clipRect;
+
+ [self getRectsBeingDrawn:&rectList count:&rectCount];
+ for (i = 0; i < rectCount; i++) {
+ clipRect = rectList[i];
+ clipRect.origin.y = (float)h - (clipRect.origin.y + clipRect.size.height);
+ clipImageRef = CGImageCreateWithImageInRect(
+ imageRef,
+ clipRect
+ );
+ CGContextDrawImage (viewContextRef, cgrect(rectList[i]), clipImageRef);
+ CGImageRelease (clipImageRef);
+ }
+ CGImageRelease (imageRef);
+ CGDataProviderRelease(dataProviderRef);
}
- (NSSize)fixAspectRatio:(NSSize)max
@@ -627,7 +595,10 @@ - (void) resizeWindow
[[self window] setContentAspectRatio:NSMakeSize(screen.width, screen.height)];
if (!([[self window] styleMask] & NSWindowStyleMaskResizable)) {
- [[self window] setContentSize:NSMakeSize(screen.width, screen.height)];
+ CGFloat width = screen.width / [[self window] backingScaleFactor];
+ CGFloat height = screen.height / [[self window] backingScaleFactor];
+
+ [[self window] setContentSize:NSMakeSize(width, height)];
[[self window] center];
} else if ([[self window] styleMask] & NSWindowStyleMaskFullScreen) {
[[self window] setContentSize:[self fixAspectRatio:[self screenSafeAreaSize]]];
@@ -637,9 +608,15 @@ - (void) resizeWindow
}
}
-- (void) updateBounds
+- (void) updateScale
{
- [self setBoundsSize:NSMakeSize(screen.width, screen.height)];
+ if (display_opengl) {
+ [[self layer] setContentsScale:[[self window] backingScaleFactor]];
+ } else {
+ [self setBoundsSize:NSMakeSize(screen.width, screen.height)];
+ }
+
+ [self updateCursorLayout];
}
#pragma clang diagnostic push
@@ -685,8 +662,8 @@ - (void) updateUIInfoLocked
info.xoff = 0;
info.yoff = 0;
- info.width = frameSize.width;
- info.height = frameSize.height;
+ info.width = frameSize.width * [[self window] backingScaleFactor];
+ info.height = frameSize.height * [[self window] backingScaleFactor];
dpy_set_ui_info(dcl.con, &info, TRUE);
}
@@ -712,28 +689,17 @@ - (void) updateUIInfo
});
}
-- (void) switchSurface:(pixman_image_t *)image
+- (void) updateScreenWidth:(int)w height:(int)h
{
- COCOA_DEBUG("QemuCocoaView: switchSurface\n");
-
- int w = pixman_image_get_width(image);
- int h = pixman_image_get_height(image);
+ COCOA_DEBUG("QemuCocoaView: updateScreenWidth:height:\n");
if (w != screen.width || h != screen.height) {
- // Resize before we trigger the redraw, or we'll redraw at the wrong size
- COCOA_DEBUG("switchSurface: new size %d x %d\n", w, h);
+ COCOA_DEBUG("updateScreenWidth:height: new size %d x %d\n", w, h);
screen.width = w;
screen.height = h;
[self resizeWindow];
- [self updateBounds];
- }
-
- // update screenBuffer
- if (pixman_image) {
- pixman_image_unref(pixman_image);
+ [self updateScale];
}
-
- pixman_image = image;
}
- (void) setFullGrab:(id)sender
@@ -1224,7 +1190,6 @@ - (void) notifyMouseModeChange {
}
}
- (BOOL) isMouseGrabbed {return isMouseGrabbed;}
-- (QEMUScreen) gscreen {return screen;}
/*
* Makes the target think all down keys are being released.
@@ -1278,7 +1243,11 @@ - (void)adjustSpeed:(id)sender;
@end
@implementation QemuCocoaAppController
+#ifdef CONFIG_OPENGL
+- (id) initWithCGL:(BOOL)cgl
+#else
- (id) init
+#endif
{
NSWindow *window;
@@ -1286,16 +1255,21 @@ - (id) init
self = [super init];
if (self) {
+ NSRect frame = NSMakeRect(0.0, 0.0, 640.0, 480.0);
// create a view and add it to the window
- cocoaView = [[QemuCocoaView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 640.0, 480.0)];
+#ifdef CONFIG_OPENGL
+ cocoaView = [[QemuCocoaView alloc] initWithFrame:frame cgl:cgl];
+#else
+ cocoaView = [[QemuCocoaView alloc] initWithFrame:frame];
+#endif
if(!cocoaView) {
error_report("(cocoa) can't create a view");
exit(1);
}
// create a window
- window = [[NSWindow alloc] initWithContentRect:[cocoaView frame]
+ window = [[NSWindow alloc] initWithContentRect:frame
styleMask:NSWindowStyleMaskTitled|NSWindowStyleMaskMiniaturizable|NSWindowStyleMaskClosable
backing:NSBackingStoreBuffered defer:NO];
if(!window) {
@@ -1389,7 +1363,7 @@ - (void)windowDidExitFullScreen:(NSNotification *)notification
- (void)windowDidResize:(NSNotification *)notification
{
- [cocoaView updateBounds];
+ [cocoaView updateScale];
[cocoaView updateUIInfo];
}
@@ -1489,13 +1463,9 @@ - (void)zoomToFit:(id) sender
- (void)toggleZoomInterpolation:(id) sender
{
- if (zoom_interpolation == kCGInterpolationNone) {
- zoom_interpolation = kCGInterpolationLow;
- [sender setState: NSControlStateValueOn];
- } else {
- zoom_interpolation = kCGInterpolationNone;
- [sender setState: NSControlStateValueOff];
- }
+ qatomic_set(&zoom_interpolation, !zoom_interpolation);
+ [sender setState:zoom_interpolation ? NSControlStateValueOn :
+ NSControlStateValueOff];
}
/* Displays the console on the screen */
@@ -1763,7 +1733,7 @@ static void create_initial_menus(void)
[menuItem setState: [[cocoaView window] styleMask] & NSWindowStyleMaskResizable ? NSControlStateValueOn : NSControlStateValueOff];
[menu addItem: menuItem];
menuItem = [[[NSMenuItem alloc] initWithTitle:@"Zoom Interpolation" action:@selector(toggleZoomInterpolation:) keyEquivalent:@""] autorelease];
- [menuItem setState: zoom_interpolation == kCGInterpolationLow ? NSControlStateValueOn : NSControlStateValueOff];
+ [menuItem setState: zoom_interpolation ? NSControlStateValueOn : NSControlStateValueOff];
[menu addItem: menuItem];
menuItem = [[[NSMenuItem alloc] initWithTitle:@"View" action:nil keyEquivalent:@""] autorelease];
[menuItem setSubmenu:menu];
@@ -2025,29 +1995,28 @@ static int cocoa_main(void)
static void cocoa_update(DisplayChangeListener *dcl,
int x, int y, int w, int h)
{
+ NSRect rect = NSMakeRect(x, surface_height(surface) - y - h, w, h);
+
COCOA_DEBUG("qemu_cocoa: cocoa_update\n");
dispatch_async(dispatch_get_main_queue(), ^{
- NSRect rect = NSMakeRect(x, [cocoaView gscreen].height - y - h, w, h);
[cocoaView setNeedsDisplayInRect:rect];
});
}
static void cocoa_switch(DisplayChangeListener *dcl,
- DisplaySurface *surface)
+ DisplaySurface *new_surface)
{
- pixman_image_t *image = surface->image;
-
COCOA_DEBUG("qemu_cocoa: cocoa_switch\n");
- // The DisplaySurface will be freed as soon as this callback returns.
- // We take a reference to the underlying pixman image here so it does
- // not disappear from under our feet; the switchSurface method will
- // deref the old image when it is done with it.
- pixman_image_ref(image);
+ surface = new_surface;
dispatch_async(dispatch_get_main_queue(), ^{
- [cocoaView switchSurface:image];
+ BQL_LOCK_GUARD();
+ int w = surface_width(surface);
+ int h = surface_height(surface);
+
+ [cocoaView updateScreenWidth:w height:h];
});
}
@@ -2087,9 +2056,224 @@ static void cocoa_cursor_define(DisplayChangeListener *dcl, QEMUCursor *cursor)
});
}
+static const DisplayChangeListenerOps dcl_ops = {
+ .dpy_name = "cocoa",
+ .dpy_gfx_update = cocoa_update,
+ .dpy_gfx_switch = cocoa_switch,
+ .dpy_refresh = cocoa_refresh,
+ .dpy_mouse_set = cocoa_mouse_set,
+ .dpy_cursor_define = cocoa_cursor_define,
+};
+
+#ifdef CONFIG_OPENGL
+
+static void with_gl_view_ctx(CodeBlock block)
+{
+#ifdef CONFIG_EGL
+ if (egl_surface) {
+ eglMakeCurrent(qemu_egl_display, egl_surface,
+ egl_surface, gl_view_ctx);
+ block();
+ eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ return;
+ }
+#endif
+
+ CGLSetCurrentContext((CGLContextObj)gl_view_ctx);
+ block();
+ CGLSetCurrentContext(NULL);
+}
+
+static CGLPixelFormatObj cocoa_gl_create_cgl_pixel_format(int bpp)
+{
+ CGLPixelFormatObj pix;
+ GLint npix;
+ CGLPixelFormatAttribute attribs[] = {
+ kCGLPFAOpenGLProfile,
+ (CGLPixelFormatAttribute)kCGLOGLPVersion_GL4_Core,
+ kCGLPFAColorSize,
+ bpp,
+ kCGLPFADoubleBuffer,
+ 0,
+ };
+
+ CGLChoosePixelFormat(attribs, &pix, &npix);
+
+ return pix;
+}
+
+static int cocoa_gl_make_context_current(DisplayGLCtx *dgc, QEMUGLContext ctx)
+{
+#ifdef CONFIG_EGL
+ if (egl_surface) {
+ EGLSurface current_surface = ctx == EGL_NO_CONTEXT ? EGL_NO_SURFACE : egl_surface;
+ return !eglMakeCurrent(qemu_egl_display, current_surface, current_surface, ctx);
+ }
+#endif
+
+ return CGLSetCurrentContext((CGLContextObj)ctx);
+}
+
+static QEMUGLContext cocoa_gl_create_context(DisplayGLCtx *dgc,
+ QEMUGLParams *params)
+{
+ CGLPixelFormatObj format;
+ CGLContextObj ctx;
+ int bpp;
+
+#ifdef CONFIG_EGL
+ if (egl_surface) {
+ eglMakeCurrent(qemu_egl_display, egl_surface,
+ egl_surface, gl_view_ctx);
+ return qemu_egl_create_context(dgc, params);
+ }
+#endif
+
+ bpp = PIXMAN_FORMAT_BPP(surface_format(surface));
+ format = cocoa_gl_create_cgl_pixel_format(bpp);
+ CGLCreateContext(format, gl_view_ctx, &ctx);
+ CGLDestroyPixelFormat(format);
+
+ return (QEMUGLContext)ctx;
+}
+
+static void cocoa_gl_destroy_context(DisplayGLCtx *dgc, QEMUGLContext ctx)
+{
+#ifdef CONFIG_EGL
+ if (egl_surface) {
+ eglDestroyContext(qemu_egl_display, ctx);
+ return;
+ }
+#endif
+
+ CGLDestroyContext(ctx);
+}
+
+static void cocoa_gl_update(DisplayChangeListener *dcl,
+ int x, int y, int w, int h)
+{
+ with_gl_view_ctx(^{
+ surface_gl_update_texture(dgc.gls, surface, x, y, w, h);
+ gl_dirty = true;
+ });
+}
+
+static void cocoa_gl_switch(DisplayChangeListener *dcl,
+ DisplaySurface *new_surface)
+{
+ with_gl_view_ctx(^{
+ surface_gl_destroy_texture(dgc.gls, surface);
+ surface_gl_create_texture(dgc.gls, new_surface);
+ });
+
+ cocoa_switch(dcl, new_surface);
+ gl_dirty = true;
+}
+
+static void cocoa_gl_render(void)
+{
+ NSSize size = [cocoaView convertSizeToBacking:[cocoaView frame].size];
+ GLint filter = qatomic_read(&zoom_interpolation) ? GL_LINEAR : GL_NEAREST;
+
+ glViewport(0, 0, size.width, size.height);
+
+ if (gl_scanout_borrow) {
+ DisplayGLTexture texture = gl_scanout_borrow(gl_scanout_id);
+
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
+ glBindTexture(GL_TEXTURE_2D, texture.id);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
+ qemu_gl_run_texture_blit(dgc.gls, texture.y_0_top);
+ } else {
+ glBindTexture(GL_TEXTURE_2D, surface->texture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
+ surface_gl_render_texture(dgc.gls, surface);
+ }
+}
+
+static void cocoa_gl_refresh(DisplayChangeListener *dcl)
+{
+ cocoa_refresh(dcl);
+
+ if (gl_dirty) {
+ gl_dirty = false;
+
+#ifdef CONFIG_EGL
+ if (egl_surface) {
+ with_gl_view_ctx(^{
+ cocoa_gl_render();
+ eglSwapBuffers(qemu_egl_display, egl_surface);
+ });
+
+ return;
+ }
+#endif
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [[cocoaView layer] setNeedsDisplay];
+ });
+ }
+}
+
+static void cocoa_gl_scanout_disable(DisplayChangeListener *dcl)
+{
+ gl_scanout_borrow = NULL;
+ gl_dirty = true;
+}
+
+static void cocoa_gl_scanout_texture(DisplayChangeListener *dcl,
+ uint32_t backing_id,
+ DisplayGLTextureBorrower backing_borrow,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
+{
+ gl_scanout_id = backing_id;
+ gl_scanout_borrow = backing_borrow;
+ gl_dirty = true;
+}
+
+static void cocoa_gl_scanout_flush(DisplayChangeListener *dcl,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
+{
+ gl_dirty = true;
+}
+
+static const DisplayChangeListenerOps dcl_gl_ops = {
+ .dpy_name = "cocoa-gl",
+ .dpy_gfx_update = cocoa_gl_update,
+ .dpy_gfx_switch = cocoa_gl_switch,
+ .dpy_gfx_check_format = console_gl_check_format,
+ .dpy_refresh = cocoa_gl_refresh,
+ .dpy_mouse_set = cocoa_mouse_set,
+ .dpy_cursor_define = cocoa_cursor_define,
+
+ .dpy_gl_scanout_disable = cocoa_gl_scanout_disable,
+ .dpy_gl_scanout_texture = cocoa_gl_scanout_texture,
+ .dpy_gl_update = cocoa_gl_scanout_flush,
+};
+
+static bool cocoa_gl_is_compatible_dcl(DisplayGLCtx *dgc,
+ DisplayChangeListener *dcl)
+{
+ return dcl->ops == &dcl_gl_ops;
+}
+
+#endif
+
+static void cocoa_display_early_init(DisplayOptions *o)
+{
+ assert(o->type == DISPLAY_TYPE_COCOA);
+ if (o->has_gl && o->gl) {
+ display_opengl = 1;
+ }
+}
+
static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+ QemuCocoaAppController *controller;
COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n");
@@ -2100,10 +2284,69 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
[QemuApplication sharedApplication];
+ dcl.con = qemu_console_lookup_default();
+ kbd = qkbd_state_init(dcl.con);
+ surface = qemu_console_surface(dcl.con);
+
// Create an Application controller
- QemuCocoaAppController *controller = [[QemuCocoaAppController alloc] init];
+#ifdef CONFIG_OPENGL
+ controller = [[QemuCocoaAppController alloc] initWithCGL:display_opengl &&
+ opts->gl != DISPLAY_GL_MODE_ES];
+#else
+ controller = [[QemuCocoaAppController alloc] init];
+#endif
[NSApp setDelegate:controller];
+ if (display_opengl) {
+#ifdef CONFIG_OPENGL
+ if (opts->gl == DISPLAY_GL_MODE_ES) {
+#ifdef CONFIG_EGL
+ if (qemu_egl_init_dpy_cocoa(DISPLAY_GL_MODE_ES)) {
+ exit(1);
+ }
+ gl_view_ctx = qemu_egl_init_ctx();
+ if (!gl_view_ctx) {
+ exit(1);
+ }
+ egl_surface = qemu_egl_init_surface(gl_view_ctx, [cocoaView layer]);
+ if (!egl_surface) {
+ exit(1);
+ }
+#else
+ error_report("OpenGLES without EGL is not supported - exiting");
+ exit(1);
+#endif
+ } else {
+ CGLPixelFormatObj format = cocoa_gl_create_cgl_pixel_format(32);
+ CGLContextObj ctx;
+ CGLCreateContext(format, NULL, &ctx);
+ CGLDestroyPixelFormat(format);
+ gl_view_ctx = (QEMUGLContext)ctx;
+#ifdef CONFIG_EGL
+ egl_surface = EGL_NO_SURFACE;
+#endif
+ cocoa_gl_make_context_current(&dgc, gl_view_ctx);
+ }
+
+ dgc.gls = qemu_gl_init_shader();
+ dcl.ops = &dcl_gl_ops;
+
+ for (unsigned int index = 0; ; index++) {
+ QemuConsole *con = qemu_console_lookup_by_index(index);
+ if (!con) {
+ break;
+ }
+
+ qemu_console_set_display_gl_ctx(con, &dgc);
+ }
+#else
+ error_report("OpenGL is not enabled - exiting");
+ exit(1);
+#endif
+ } else {
+ dcl.ops = &dcl_ops;
+ }
+
/* if fullscreen mode is to be used */
if (opts->has_full_screen && opts->full_screen) {
[[cocoaView window] toggleFullScreen: nil];
@@ -2127,9 +2370,8 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
[cocoaView window].styleMask |= NSWindowStyleMaskResizable;
}
- if (opts->u.cocoa.has_zoom_interpolation && opts->u.cocoa.zoom_interpolation) {
- zoom_interpolation = kCGInterpolationLow;
- }
+ zoom_interpolation = opts->u.cocoa.has_zoom_interpolation &&
+ opts->u.cocoa.zoom_interpolation;
create_initial_menus();
/*
@@ -2142,9 +2384,6 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
add_console_menu_entries();
addRemovableDevicesMenuItems();
- dcl.con = qemu_console_lookup_default();
- kbd = qkbd_state_init(dcl.con);
-
// register vga output callbacks
register_displaychangelistener(&dcl);
qemu_add_mouse_mode_change_notifier(&mouse_mode_change_notifier);
@@ -2166,6 +2405,7 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
static QemuDisplay qemu_display_cocoa = {
.type = DISPLAY_TYPE_COCOA,
+ .early_init = cocoa_display_early_init,
.init = cocoa_display_init,
};
@@ -2175,3 +2415,7 @@ static void register_cocoa(void)
}
type_init(register_cocoa);
+
+#ifdef CONFIG_OPENGL
+module_dep("ui-opengl");
+#endif
diff --git a/ui/console.c b/ui/console.c
index 2d00828..c8c09d1 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -288,14 +288,11 @@ static void displaychangelistener_display_console(DisplayChangeListener *dcl,
dcl->ops->dpy_gl_scanout_texture) {
dcl->ops->dpy_gl_scanout_texture(dcl,
con->scanout.texture.backing_id,
- con->scanout.texture.backing_y_0_top,
- con->scanout.texture.backing_width,
- con->scanout.texture.backing_height,
+ con->scanout.texture.backing_borrow,
con->scanout.texture.x,
con->scanout.texture.y,
con->scanout.texture.width,
- con->scanout.texture.height,
- con->scanout.texture.d3d_tex2d);
+ con->scanout.texture.height);
}
}
@@ -1018,31 +1015,25 @@ void dpy_gl_scanout_disable(QemuConsole *con)
void dpy_gl_scanout_texture(QemuConsole *con,
uint32_t backing_id,
- bool backing_y_0_top,
- uint32_t backing_width,
- uint32_t backing_height,
+ DisplayGLTextureBorrower backing_borrow,
uint32_t x, uint32_t y,
- uint32_t width, uint32_t height,
- void *d3d_tex2d)
+ uint32_t width, uint32_t height)
{
DisplayState *s = con->ds;
DisplayChangeListener *dcl;
con->scanout.kind = SCANOUT_TEXTURE;
con->scanout.texture = (ScanoutTexture) {
- backing_id, backing_y_0_top, backing_width, backing_height,
- x, y, width, height, d3d_tex2d,
+ backing_id, backing_borrow,
+ x, y, width, height
};
QLIST_FOREACH(dcl, &s->listeners, next) {
if (con != dcl->con) {
continue;
}
if (dcl->ops->dpy_gl_scanout_texture) {
- dcl->ops->dpy_gl_scanout_texture(dcl, backing_id,
- backing_y_0_top,
- backing_width, backing_height,
- x, y, width, height,
- d3d_tex2d);
+ dcl->ops->dpy_gl_scanout_texture(dcl, backing_id, backing_borrow,
+ x, y, width, height);
}
}
}
@@ -1487,12 +1478,7 @@ void qemu_console_resize(QemuConsole *s, int width, int height)
DisplaySurface *qemu_console_surface(QemuConsole *console)
{
- switch (console->scanout.kind) {
- case SCANOUT_SURFACE:
- return console->surface;
- default:
- return NULL;
- }
+ return console->surface;
}
PixelFormat qemu_default_pixelformat(int bpp)
diff --git a/ui/dbus-console.c b/ui/dbus-console.c
index 85e215e..9352286 100644
--- a/ui/dbus-console.c
+++ b/ui/dbus-console.c
@@ -93,13 +93,10 @@ dbus_gl_scanout_disable(DisplayChangeListener *dcl)
static void
dbus_gl_scanout_texture(DisplayChangeListener *dcl,
- uint32_t tex_id,
- bool backing_y_0_top,
- uint32_t backing_width,
- uint32_t backing_height,
+ uint32_t backing_id,
+ DisplayGLTextureBorrower backing_borrow,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h,
- void *d3d_tex2d)
+ uint32_t w, uint32_t h)
{
DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl);
diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c
index 42875b8..cce1c5c 100644
--- a/ui/dbus-listener.c
+++ b/ui/dbus-listener.c
@@ -572,14 +572,14 @@ static bool dbus_scanout_map(DBusDisplayListener *ddl)
#endif /* WIN32 */
#ifdef CONFIG_OPENGL
-static void dbus_scanout_texture(DisplayChangeListener *dcl,
- uint32_t tex_id,
- bool backing_y_0_top,
- uint32_t backing_width,
- uint32_t backing_height,
- uint32_t x, uint32_t y,
- uint32_t w, uint32_t h,
- void *d3d_tex2d)
+static void dbus_scanout_borrowed_texture(DisplayChangeListener *dcl,
+ uint32_t tex_id,
+ bool backing_y_0_top,
+ uint32_t backing_width,
+ uint32_t backing_height,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h,
+ void *d3d_tex2d)
{
trace_dbus_scanout_texture(tex_id, backing_y_0_top,
backing_width, backing_height, x, y, w, h);
@@ -620,6 +620,19 @@ static void dbus_scanout_texture(DisplayChangeListener *dcl,
#endif
}
+static void dbus_scanout_texture(DisplayChangeListener *dcl,
+ uint32_t backing_id,
+ DisplayGLTextureBorrower backing_borrow,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
+{
+ DisplayGLTexture tex = backing_borrow(backing_id);
+
+ dbus_scanout_borrowed_texture(dcl, tex.id, tex.y_0_top,
+ tex.width, tex.height,
+ x, y, w, h, tex.d3d_tex2d);
+}
+
#ifdef CONFIG_GBM
static void dbus_cursor_dmabuf(DisplayChangeListener *dcl,
QemuDmaBuf *dmabuf, bool have_hot,
@@ -859,8 +872,9 @@ static void dbus_gl_gfx_switch(DisplayChangeListener *dcl,
int height = surface_height(ddl->ds);
/* TODO: lazy send dmabuf (there are unnecessary sent otherwise) */
- dbus_scanout_texture(&ddl->dcl, ddl->ds->texture, false,
- width, height, 0, 0, width, height, NULL);
+ dbus_scanout_borrowed_texture(&ddl->dcl, ddl->ds->texture, false,
+ width, height, 0, 0, width, height,
+ NULL);
}
}
#endif
diff --git a/ui/egl-headless.c b/ui/egl-headless.c
index 1f6b845..6a20c6a 100644
--- a/ui/egl-headless.c
+++ b/ui/egl-headless.c
@@ -55,14 +55,11 @@ static void egl_scanout_disable(DisplayChangeListener *dcl)
egl_fb_destroy(&edpy->blit_fb);
}
-static void egl_scanout_texture(DisplayChangeListener *dcl,
- uint32_t backing_id,
- bool backing_y_0_top,
- uint32_t backing_width,
- uint32_t backing_height,
- uint32_t x, uint32_t y,
- uint32_t w, uint32_t h,
- void *d3d_tex2d)
+static void egl_scanout_imported_texture(DisplayChangeListener *dcl,
+ uint32_t backing_texture,
+ bool backing_y_0_top,
+ uint32_t backing_width,
+ uint32_t backing_height)
{
egl_dpy *edpy = container_of(dcl, egl_dpy, dcl);
@@ -70,7 +67,7 @@ static void egl_scanout_texture(DisplayChangeListener *dcl,
/* source framebuffer */
egl_fb_setup_for_tex(&edpy->guest_fb,
- backing_width, backing_height, backing_id, false);
+ backing_width, backing_height, backing_texture, false);
/* dest framebuffer */
if (edpy->blit_fb.width != backing_width ||
@@ -80,6 +77,20 @@ static void egl_scanout_texture(DisplayChangeListener *dcl,
}
}
+static void egl_scanout_texture(DisplayChangeListener *dcl,
+ uint32_t backing_id,
+ DisplayGLTextureBorrower backing_borrow,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
+{
+ DisplayGLTexture backing_texture = backing_borrow(backing_id);
+
+ egl_scanout_imported_texture(dcl, backing_texture.id,
+ backing_texture.y_0_top,
+ backing_texture.width,
+ backing_texture.height);
+}
+
#ifdef CONFIG_GBM
static void egl_scanout_dmabuf(DisplayChangeListener *dcl,
@@ -96,8 +107,7 @@ static void egl_scanout_dmabuf(DisplayChangeListener *dcl,
width = qemu_dmabuf_get_width(dmabuf);
height = qemu_dmabuf_get_height(dmabuf);
- egl_scanout_texture(dcl, texture, false, width, height, 0, 0,
- width, height, NULL);
+ egl_scanout_imported_texture(dcl, texture, false, width, height);
}
static void egl_cursor_dmabuf(DisplayChangeListener *dcl,
diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c
index e3f2872..f1af43a 100644
--- a/ui/egl-helpers.c
+++ b/ui/egl-helpers.c
@@ -466,7 +466,7 @@ void egl_dmabuf_create_fence(QemuDmaBuf *dmabuf)
/* ---------------------------------------------------------------------- */
-EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, EGLNativeWindowType win)
+EGLSurface qemu_egl_init_surface(EGLContext ectx, EGLNativeWindowType win)
{
EGLSurface esurface;
EGLBoolean b;
@@ -490,6 +490,70 @@ EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, EGLNativeWindowType win)
/* ---------------------------------------------------------------------- */
+static int qemu_egl_init_dpy(EGLDisplay dpy, DisplayGLMode mode)
+{
+ static const EGLint conf_att_core[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
+ EGL_RED_SIZE, 5,
+ EGL_GREEN_SIZE, 5,
+ EGL_BLUE_SIZE, 5,
+ EGL_ALPHA_SIZE, 0,
+ EGL_NONE,
+ };
+ static const EGLint conf_att_gles[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 5,
+ EGL_GREEN_SIZE, 5,
+ EGL_BLUE_SIZE, 5,
+ EGL_ALPHA_SIZE, 0,
+ EGL_NONE,
+ };
+ EGLint major, minor;
+ EGLBoolean b;
+ EGLint n;
+ bool gles = (mode == DISPLAY_GL_MODE_ES);
+
+ qemu_egl_display = dpy;
+
+ b = eglInitialize(qemu_egl_display, &major, &minor);
+ if (b == EGL_FALSE) {
+ error_report("egl: eglInitialize failed");
+ return -1;
+ }
+
+ b = eglBindAPI(gles ? EGL_OPENGL_ES_API : EGL_OPENGL_API);
+ if (b == EGL_FALSE) {
+ error_report("egl: eglBindAPI failed (%s mode)",
+ gles ? "gles" : "core");
+ return -1;
+ }
+
+ b = eglChooseConfig(qemu_egl_display,
+ gles ? conf_att_gles : conf_att_core,
+ &qemu_egl_config, 1, &n);
+ if (b == EGL_FALSE || n != 1) {
+ error_report("egl: eglChooseConfig failed (%s mode)",
+ gles ? "gles" : "core");
+ return -1;
+ }
+
+ qemu_egl_mode = gles ? DISPLAY_GL_MODE_ES : DISPLAY_GL_MODE_CORE;
+ return 0;
+}
+
+int qemu_egl_init_dpy_cocoa(DisplayGLMode mode)
+{
+ EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (dpy == EGL_NO_DISPLAY) {
+ error_report("egl: eglGetDisplay failed");
+ return -1;
+ }
+
+ return qemu_egl_init_dpy(dpy, mode);
+}
+
#if defined(CONFIG_X11) || defined(CONFIG_GBM) || defined(WIN32)
/*
@@ -520,8 +584,9 @@ EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, EGLNativeWindowType win)
* platform extensions (EGL_KHR_platform_gbm and friends) yet it doesn't seem
* like mesa will be able to advertise these (even though it can do EGL 1.5).
*/
-static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native,
- EGLenum platform)
+static int qemu_egl_init_dpy_platform(EGLNativeDisplayType native,
+ EGLenum platform,
+ DisplayGLMode mode)
{
EGLDisplay dpy = EGL_NO_DISPLAY;
@@ -536,66 +601,13 @@ static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native,
/* fallback */
dpy = eglGetDisplay(native);
}
- return dpy;
-}
-static int qemu_egl_init_dpy(EGLNativeDisplayType dpy,
- EGLenum platform,
- DisplayGLMode mode)
-{
- static const EGLint conf_att_core[] = {
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
- EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
- EGL_RED_SIZE, 5,
- EGL_GREEN_SIZE, 5,
- EGL_BLUE_SIZE, 5,
- EGL_ALPHA_SIZE, 0,
- EGL_NONE,
- };
- static const EGLint conf_att_gles[] = {
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL_RED_SIZE, 5,
- EGL_GREEN_SIZE, 5,
- EGL_BLUE_SIZE, 5,
- EGL_ALPHA_SIZE, 0,
- EGL_NONE,
- };
- EGLint major, minor;
- EGLBoolean b;
- EGLint n;
- bool gles = (mode == DISPLAY_GL_MODE_ES);
-
- qemu_egl_display = qemu_egl_get_display(dpy, platform);
- if (qemu_egl_display == EGL_NO_DISPLAY) {
- error_report("egl: eglGetDisplay failed: %s", qemu_egl_get_error_string());
- return -1;
- }
-
- b = eglInitialize(qemu_egl_display, &major, &minor);
- if (b == EGL_FALSE) {
- error_report("egl: eglInitialize failed: %s", qemu_egl_get_error_string());
- return -1;
- }
-
- b = eglBindAPI(gles ? EGL_OPENGL_ES_API : EGL_OPENGL_API);
- if (b == EGL_FALSE) {
- error_report("egl: eglBindAPI failed (%s mode): %s",
- gles ? "gles" : "core", qemu_egl_get_error_string());
- return -1;
- }
-
- b = eglChooseConfig(qemu_egl_display,
- gles ? conf_att_gles : conf_att_core,
- &qemu_egl_config, 1, &n);
- if (b == EGL_FALSE || n != 1) {
- error_report("egl: eglChooseConfig failed (%s mode): %s",
- gles ? "gles" : "core", qemu_egl_get_error_string());
+ if (dpy == EGL_NO_DISPLAY) {
+ error_report("egl: eglGetDisplay failed");
return -1;
}
- qemu_egl_mode = gles ? DISPLAY_GL_MODE_ES : DISPLAY_GL_MODE_CORE;
- return 0;
+ return qemu_egl_init_dpy(dpy, mode);
}
#endif
@@ -604,18 +616,18 @@ static int qemu_egl_init_dpy(EGLNativeDisplayType dpy,
int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode)
{
#ifdef EGL_KHR_platform_x11
- return qemu_egl_init_dpy(dpy, EGL_PLATFORM_X11_KHR, mode);
+ return qemu_egl_init_dpy_platform(dpy, EGL_PLATFORM_X11_KHR, mode);
#else
- return qemu_egl_init_dpy(dpy, 0, mode);
+ return qemu_egl_init_dpy_platform(dpy, 0, mode);
#endif
}
int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode)
{
#ifdef EGL_MESA_platform_gbm
- return qemu_egl_init_dpy(dpy, EGL_PLATFORM_GBM_MESA, mode);
+ return qemu_egl_init_dpy_platform(dpy, EGL_PLATFORM_GBM_MESA, mode);
#else
- return qemu_egl_init_dpy(dpy, 0, mode);
+ return qemu_egl_init_dpy_platform(dpy, 0, mode);
#endif
}
#endif
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 0b787be..6c81f7b 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -57,7 +57,7 @@ void gd_egl_init(VirtualConsole *vc)
}
vc->gfx.ectx = qemu_egl_init_ctx();
- vc->gfx.esurface = qemu_egl_init_surface_x11
+ vc->gfx.esurface = qemu_egl_init_surface
(vc->gfx.ectx, (EGLNativeWindowType)x11_window);
assert(vc->gfx.esurface);
@@ -237,15 +237,14 @@ void gd_egl_scanout_disable(DisplayChangeListener *dcl)
gtk_egl_set_scanout_mode(vc, false);
}
-void gd_egl_scanout_texture(DisplayChangeListener *dcl,
- uint32_t backing_id, bool backing_y_0_top,
- uint32_t backing_width, uint32_t backing_height,
- uint32_t x, uint32_t y,
- uint32_t w, uint32_t h,
- void *d3d_tex2d)
+static void gd_egl_scanout_borrowed_texture(VirtualConsole *vc,
+ uint32_t backing_id,
+ bool backing_y_0_top,
+ uint32_t backing_width,
+ uint32_t backing_height,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
{
- VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
-
vc->gfx.x = x;
vc->gfx.y = y;
vc->gfx.w = w;
@@ -267,8 +266,22 @@ void gd_egl_scanout_texture(DisplayChangeListener *dcl,
backing_id, false);
}
-void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
- QemuDmaBuf *dmabuf)
+void gd_egl_scanout_texture(DisplayChangeListener *dcl, uint32_t backing_id,
+ DisplayGLTextureBorrower backing_borrow,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+ DisplayGLTexture backing_texture = backing_borrow(backing_id);
+
+ gd_egl_scanout_borrowed_texture(vc, backing_texture.id,
+ backing_texture.y_0_top,
+ backing_texture.width,
+ backing_texture.height,
+ x, y, w, h);
+}
+
+void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl, QemuDmaBuf *dmabuf)
{
#ifdef CONFIG_GBM
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
@@ -292,8 +305,8 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl,
backing_height = qemu_dmabuf_get_backing_height(dmabuf);
y0_top = qemu_dmabuf_get_y0_top(dmabuf);
- gd_egl_scanout_texture(dcl, texture, y0_top, backing_width, backing_height,
- x, y, width, height, NULL);
+ gd_egl_scanout_borrowed_texture(vc, texture, y0_top, backing_width,
+ backing_height, x, y, width, height);
if (qemu_dmabuf_get_allow_fences(dmabuf)) {
vc->gfx.guest_fb.dmabuf = dmabuf;
diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
index 8151cc4..825d84f 100644
--- a/ui/gtk-gl-area.c
+++ b/ui/gtk-gl-area.c
@@ -287,17 +287,14 @@ void gd_gl_area_destroy_context(DisplayGLCtx *dgc, QEMUGLContext ctx)
g_clear_object(&ctx);
}
-void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
- uint32_t backing_id,
- bool backing_y_0_top,
- uint32_t backing_width,
- uint32_t backing_height,
- uint32_t x, uint32_t y,
- uint32_t w, uint32_t h,
- void *d3d_tex2d)
+static void gd_gl_area_scanout_borrowed_texture(VirtualConsole *vc,
+ uint32_t backing_id,
+ bool backing_y_0_top,
+ uint32_t backing_width,
+ uint32_t backing_height,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
{
- VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
-
vc->gfx.x = x;
vc->gfx.y = y;
vc->gfx.w = w;
@@ -306,7 +303,7 @@ void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
- if (backing_id == 0 || vc->gfx.w == 0 || vc->gfx.h == 0) {
+ if (vc->gfx.w == 0 || vc->gfx.h == 0) {
gtk_gl_area_set_scanout_mode(vc, false);
return;
}
@@ -316,6 +313,20 @@ void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
backing_id, false);
}
+void gd_gl_area_scanout_texture(DisplayChangeListener *dcl,
+ uint32_t backing_id,
+ DisplayGLTextureBorrower backing_borrow,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+ DisplayGLTexture texture = backing_borrow(backing_id);
+
+ gd_gl_area_scanout_borrowed_texture(vc, texture.id, texture.y_0_top,
+ texture.width, texture.height,
+ x, y, w, h);
+}
+
void gd_gl_area_scanout_disable(DisplayChangeListener *dcl)
{
VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
@@ -360,9 +371,8 @@ void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl,
backing_height = qemu_dmabuf_get_backing_height(dmabuf);
y0_top = qemu_dmabuf_get_y0_top(dmabuf);
- gd_gl_area_scanout_texture(dcl, texture, y0_top,
- backing_width, backing_height,
- x, y, width, height, NULL);
+ gd_gl_area_scanout_borrowed_texture(vc, texture, y0_top, backing_width,
+ backing_height, x, y, width, height);
if (qemu_dmabuf_get_allow_fences(dmabuf)) {
vc->gfx.guest_fb.dmabuf = dmabuf;
diff --git a/ui/gtk.c b/ui/gtk.c
index e91d093..47d6fb2 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -342,7 +342,7 @@ static void gd_update_full_redraw(VirtualConsole *vc)
int ww, wh;
ww = gdk_window_get_width(gtk_widget_get_window(area));
wh = gdk_window_get_height(gtk_widget_get_window(area));
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
if (vc->gfx.gls && gtk_use_gl_area) {
gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
return;
@@ -563,7 +563,7 @@ static const DisplayChangeListenerOps dcl_ops = {
};
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
static bool gd_has_dmabuf(DisplayChangeListener *dcl)
{
@@ -674,7 +674,7 @@ static const DisplayGLCtxOps egl_ctx_ops = {
};
#endif
-#endif /* CONFIG_OPENGL */
+#endif /* defined(CONFIG_OPENGL) && defined(CONFIG_EGL) */
/** QEMU Events **/
@@ -752,7 +752,7 @@ static void gd_set_ui_size(VirtualConsole *vc, gint width, gint height)
dpy_set_ui_info(vc->gfx.dcl.con, &info, true);
}
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
static gboolean gd_render_event(GtkGLArea *area, GdkGLContext *context,
void *opaque)
@@ -906,7 +906,7 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
int ww_widget, wh_widget, ww_surface, wh_surface;
int fbw, fbh;
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
if (vc->gfx.gls) {
if (gtk_use_gl_area) {
/* invoke render callback please */
@@ -1461,7 +1461,7 @@ static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event,
vc->tab_item, vc->label);
gtk_widget_destroy(vc->window);
vc->window = NULL;
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
if (vc->gfx.esurface) {
eglDestroySurface(qemu_egl_display, vc->gfx.esurface);
vc->gfx.esurface = NULL;
@@ -1500,7 +1500,7 @@ static void gd_menu_untabify(GtkMenuItem *item, void *opaque)
if (!vc->window) {
gtk_widget_set_sensitive(vc->menu_item, false);
vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
if (vc->gfx.esurface) {
eglDestroySurface(qemu_egl_display, vc->gfx.esurface);
vc->gfx.esurface = NULL;
@@ -2112,7 +2112,7 @@ static void gd_connect_vc_gfx_signals(VirtualConsole *vc)
{
g_signal_connect(vc->gfx.drawing_area, "draw",
G_CALLBACK(gd_draw_event), vc);
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
if (gtk_use_gl_area) {
/* wire up GtkGlArea events */
g_signal_connect(vc->gfx.drawing_area, "render",
@@ -2228,7 +2228,7 @@ static GtkWidget *gd_create_menu_machine(GtkDisplayState *s)
return machine_menu;
}
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
static void gl_area_realize(GtkGLArea *area, VirtualConsole *vc)
{
gtk_gl_area_make_current(area);
@@ -2267,7 +2267,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
vc->gfx.scale_x = vc->gfx.preferred_scale;
vc->gfx.scale_y = vc->gfx.preferred_scale;
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
if (display_opengl) {
if (gtk_use_gl_area) {
vc->gfx.drawing_area = gtk_gl_area_new();
@@ -2635,7 +2635,7 @@ static void early_gtk_display_init(DisplayOptions *opts)
assert(opts->type == DISPLAY_TYPE_GTK);
if (opts->has_gl && opts->gl != DISPLAY_GL_MODE_OFF) {
-#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
#if defined(GDK_WINDOWING_WAYLAND)
if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
gtk_use_gl_area = true;
@@ -2678,6 +2678,6 @@ static void register_gtk(void)
type_init(register_gtk);
-#ifdef CONFIG_OPENGL
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
module_dep("ui-opengl");
#endif
diff --git a/ui/meson.build b/ui/meson.build
index 6371422..5cec901 100644
--- a/ui/meson.build
+++ b/ui/meson.build
@@ -59,14 +59,16 @@ if opengl.found()
opengl_ss = ss.source_set()
opengl_ss.add(gbm, pixman)
opengl_ss.add(when: [opengl],
- if_true: files('shader.c', 'console-gl.c', 'egl-helpers.c', 'egl-context.c'))
+ if_true: files('shader.c', 'console-gl.c'))
+ opengl_ss.add(when: [egl, opengl],
+ if_true: files('egl-helpers.c', 'egl-context.c'))
ui_modules += {'opengl' : opengl_ss}
endif
if opengl.found()
egl_headless_ss = ss.source_set()
- egl_headless_ss.add(when: [opengl, pixman],
- if_true: [files('egl-headless.c'), gbm])
+ egl_headless_ss.add(when: [egl, opengl, pixman],
+ if_true: files('egl-headless.c'))
ui_modules += {'egl-headless' : egl_headless_ss}
endif
@@ -111,8 +113,8 @@ if gtk.found()
gtk_ss.add(files('gtk-clipboard.c'))
endif
gtk_ss.add(when: x11, if_true: files('x_keymap.c'))
- gtk_ss.add(when: opengl, if_true: files('gtk-gl-area.c'))
- gtk_ss.add(when: [x11, opengl], if_true: files('gtk-egl.c'))
+ gtk_ss.add(when: [egl, opengl], if_true: files('gtk-gl-area.c'))
+ gtk_ss.add(when: [egl, x11, opengl], if_true: files('gtk-egl.c'))
ui_modules += {'gtk' : gtk_ss}
endif
@@ -123,7 +125,7 @@ if sdl.found()
'sdl2-input.c',
'sdl2.c',
))
- sdl_ss.add(when: opengl, if_true: files('sdl2-gl.c'))
+ sdl_ss.add(when: [egl, opengl], if_true: files('sdl2-gl.c'))
sdl_ss.add(when: x11, if_true: files('x_keymap.c'))
ui_modules += {'sdl' : sdl_ss}
endif
diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c
index 3be17d1..99a4171 100644
--- a/ui/sdl2-gl.c
+++ b/ui/sdl2-gl.c
@@ -201,27 +201,25 @@ void sdl2_gl_scanout_disable(DisplayChangeListener *dcl)
void sdl2_gl_scanout_texture(DisplayChangeListener *dcl,
uint32_t backing_id,
- bool backing_y_0_top,
- uint32_t backing_width,
- uint32_t backing_height,
+ DisplayGLTextureBorrower backing_borrow,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h,
- void *d3d_tex2d)
+ uint32_t w, uint32_t h)
{
struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
+ DisplayGLTexture texture = backing_borrow(backing_id);
assert(scon->opengl);
scon->x = x;
scon->y = y;
scon->w = w;
scon->h = h;
- scon->y0_top = backing_y_0_top;
+ scon->y0_top = texture.y_0_top;
SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
sdl2_set_scanout_mode(scon, true);
- egl_fb_setup_for_tex(&scon->guest_fb, backing_width, backing_height,
- backing_id, false);
+ egl_fb_setup_for_tex(&scon->guest_fb, texture.width, texture.height,
+ texture.id, false);
}
void sdl2_gl_scanout_flush(DisplayChangeListener *dcl,
diff --git a/ui/sdl2.c b/ui/sdl2.c
index b00e421..dff6997 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -93,7 +93,7 @@ void sdl2_window_create(struct sdl2_console *scon)
if (scon->hidden) {
flags |= SDL_WINDOW_HIDDEN;
}
-#ifdef CONFIG_OPENGL
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
if (scon->opengl) {
flags |= SDL_WINDOW_OPENGL;
}
@@ -155,7 +155,7 @@ void sdl2_window_resize(struct sdl2_console *scon)
static void sdl2_redraw(struct sdl2_console *scon)
{
if (scon->opengl) {
-#ifdef CONFIG_OPENGL
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
sdl2_gl_redraw(scon);
#endif
} else {
@@ -795,7 +795,7 @@ static const DisplayChangeListenerOps dcl_2d_ops = {
.dpy_cursor_define = sdl_mouse_define,
};
-#ifdef CONFIG_OPENGL
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
static const DisplayChangeListenerOps dcl_gl_ops = {
.dpy_name = "sdl2-gl",
.dpy_gfx_update = sdl2_gl_update,
@@ -829,7 +829,7 @@ static void sdl2_display_early_init(DisplayOptions *o)
{
assert(o->type == DISPLAY_TYPE_SDL);
if (o->has_gl && o->gl) {
-#ifdef CONFIG_OPENGL
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
display_opengl = 1;
#endif
}
@@ -896,7 +896,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
}
sdl2_console[i].idx = i;
sdl2_console[i].opts = o;
-#ifdef CONFIG_OPENGL
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
sdl2_console[i].opengl = display_opengl;
sdl2_console[i].dcl.ops = display_opengl ? &dcl_gl_ops : &dcl_2d_ops;
sdl2_console[i].dgc.ops = display_opengl ? &gl_ctx_ops : NULL;
@@ -968,6 +968,6 @@ static void register_sdl1(void)
type_init(register_sdl1);
-#ifdef CONFIG_OPENGL
+#if defined(CONFIG_OPENGL) && defined(CONFIG_EGL)
module_dep("ui-opengl");
#endif
diff --git a/ui/shader.c b/ui/shader.c
index ab448c4..c5c9f75 100644
--- a/ui/shader.c
+++ b/ui/shader.c
@@ -152,11 +152,19 @@ end:
QemuGLShader *qemu_gl_init_shader(void)
{
QemuGLShader *gls = g_new0(QemuGLShader, 1);
-
+ const char *header = epoxy_is_desktop_gl() ? "#version 140\n" : "#version 300 es\n";
+ char vert_src[256];
+ char frag_src[256];
+ char *vert_src_body = stpcpy(vert_src, header);
+ char *frag_src_body = stpcpy(frag_src, header);
+
+ strcpy(vert_src_body, texture_blit_vert_src);
+ strcpy(frag_src_body, texture_blit_frag_src);
gls->texture_blit_prog = qemu_gl_create_compile_link_program
- (texture_blit_vert_src, texture_blit_frag_src);
+ (vert_src, frag_src);
+ strcpy(vert_src_body, texture_blit_flip_vert_src);
gls->texture_blit_flip_prog = qemu_gl_create_compile_link_program
- (texture_blit_flip_vert_src, texture_blit_frag_src);
+ (vert_src, frag_src);
if (!gls->texture_blit_prog || !gls->texture_blit_flip_prog) {
exit(1);
}
diff --git a/ui/shader/texture-blit-flip.vert b/ui/shader/texture-blit-flip.vert
index f7a448d..1e4ac4c 100644
--- a/ui/shader/texture-blit-flip.vert
+++ b/ui/shader/texture-blit-flip.vert
@@ -1,5 +1,3 @@
-#version 300 es
-
in vec2 in_position;
out vec2 ex_tex_coord;
diff --git a/ui/shader/texture-blit.frag b/ui/shader/texture-blit.frag
index 8ed95a4..bd296a2 100644
--- a/ui/shader/texture-blit.frag
+++ b/ui/shader/texture-blit.frag
@@ -1,5 +1,3 @@
-#version 300 es
-
uniform sampler2D image;
in mediump vec2 ex_tex_coord;
out mediump vec4 out_frag_color;
diff --git a/ui/shader/texture-blit.vert b/ui/shader/texture-blit.vert
index fb48d70..ae205f6 100644
--- a/ui/shader/texture-blit.vert
+++ b/ui/shader/texture-blit.vert
@@ -1,5 +1,3 @@
-#version 300 es
-
in vec2 in_position;
out vec2 ex_tex_coord;
diff --git a/ui/spice-display.c b/ui/spice-display.c
index db71e86..a8bd8fe 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -1078,21 +1078,19 @@ static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl)
}
static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl,
- uint32_t tex_id,
- bool y_0_top,
- uint32_t backing_width,
- uint32_t backing_height,
+ uint32_t backing_id,
+ DisplayGLTextureBorrower backing_borrow,
uint32_t x, uint32_t y,
- uint32_t w, uint32_t h,
- void *d3d_tex2d)
+ uint32_t w, uint32_t h)
{
SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
EGLint offset[DMABUF_MAX_PLANES], stride[DMABUF_MAX_PLANES], fourcc = 0;
int fd[DMABUF_MAX_PLANES], num_planes, i;
uint64_t modifier;
+ DisplayGLTexture tex = backing_borrow(backing_id);
- assert(tex_id);
- if (!egl_dmabuf_export_texture(tex_id, fd, offset, stride, &fourcc,
+ assert(tex.id);
+ if (!egl_dmabuf_export_texture(tex.id, fd, offset, stride, &fourcc,
&num_planes, &modifier)) {
fprintf(stderr, "%s: failed to export dmabuf for texture\n", __func__);
return;
@@ -1103,9 +1101,9 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl,
if (spice_remote_client && modifier != DRM_FORMAT_MOD_LINEAR) {
egl_fb_destroy(&ssd->guest_fb);
egl_fb_setup_for_tex(&ssd->guest_fb,
- backing_width, backing_height,
- tex_id, false);
- ssd->backing_y_0_top = y_0_top;
+ tex.width, tex.height,
+ tex.id, false);
+ ssd->backing_y_0_top = tex.y_0_top;
ssd->blit_scanout_texture = true;
ssd->new_scanout_texture = true;
@@ -1114,9 +1112,9 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl,
}
} else {
/* note: spice server will close the fd */
- spice_server_gl_scanout(&ssd->qxl, fd, backing_width, backing_height,
+ spice_server_gl_scanout(&ssd->qxl, fd, tex.width, tex.height,
(uint32_t *)offset, (uint32_t *)stride,
- num_planes, fourcc, modifier, y_0_top);
+ num_planes, fourcc, modifier, tex.y_0_top);
qemu_spice_gl_monitor_config(ssd, x, y, w, h);
}
#!/bin/bash
set -euo pipefail
macos_min_version="12.0"
angle_commit="ccaea6b265"
chromium_build_commit="dd54bc718b7c5363155660d12b7965ea9f87ada9"
chromium_testing_commit="6d914f364e23232b935ac9fb3a615065b716da13"
vulkan_headers_commit="d1cd37e925510a167d4abef39340dbdea47d8989"
chromium_zlib_commit="85f05b0835f934e52772efc308baa80cdd491838"
chromium_jsoncpp_commit="f62d44704b4da6014aa231cfc116e7fd29617d2a"
jsoncpp_source_commit="42e892d96e47b1f6e29844cc705e148ec4856448"
spirv_headers_commit="01e0577914a75a2569c846778c2f93aa8e6feddd"
spirv_tools_commit="d7ac0e0fd062953f946169304456b58e36c32778"
astc_encoder_commit="2319d9c4d4af53a7fc7c52985e264ce6e8a02a9b"
libepoxy_commit="47d975d"
virglrenderer_commit="1.2.0"
qemu_commit="v10.1.2"
command -v gn >/dev/null 2>&1 || { echo "gn not found. Install: brew install gn"; exit 1; }
command -v ninja >/dev/null 2>&1 || { echo "ninja not found. Install: brew install ninja"; exit 1; }
command -v meson >/dev/null 2>&1 || { echo "meson not found. Install: brew install meson"; exit 1; }
command -v pkg-config >/dev/null 2>&1 || { echo "pkg-config not found. Install: brew install pkg-config"; exit 1; }
port installed rapidjson >/dev/null 2>&1 || { echo "rapidjson not found. Install: sudo port install rapidjson"; exit 1; }
export CC=clang
export CXX=clang++
mkdir -p build/qemu source/angle source/libepoxy source/virglrenderer source/qemu var
if [[ ! -d source/angle/.git ]]; then
git -C source/angle init
git -C source/angle fetch https://chromium.googlesource.com/angle/angle
git -C source/angle checkout FETCH_HEAD
fi
if [[ ! -f source/angle/build/dotfile_settings.gni ]]; then
echo "Downloading Chromium build files..."
rm -rf source/angle/build
git clone --depth 1 https://github.com/gsource-mirror/chromium-src-build.git source/angle/build
cd source/angle/build
git fetch --depth 1 origin ${chromium_build_commit}
git checkout ${chromium_build_commit}
cd ../../..
# Create gclient_args.gni with declare_args() wrapper (required by newer ANGLE)
cat > source/angle/build/config/gclient_args.gni << 'EOF'
# Generated from DEPS for ANGLE standalone build
declare_args() {
checkout_angle_internal = false
checkout_angle_mesa = false
checkout_angle_restricted_traces = false
generate_location_tags = false
checkout_android = false
checkout_android_native_support = false
checkout_google_benchmark = false
checkout_openxr = false
checkout_telemetry_dependencies = false
}
EOF
# Apply MacPorts-style patches
sed -i.bak '/^ prefix = rebase_path/s|^|# |' source/angle/build/toolchain/apple/toolchain.gni
sed -i.bak '/^ compiler_prefix = /s|^|# |' source/angle/build/toolchain/apple/toolchain.gni
sed -i.bak '/^ compiler_prefix = /s|^|# |' source/angle/build/toolchain/apple/toolchain.gni
sed -i.bak 's|_cc = "${prefix}clang"|_cc = "clang"|' source/angle/build/toolchain/apple/toolchain.gni
sed -i.bak 's|_cxx = "${prefix}clang++"|_cxx = "clang++"|' source/angle/build/toolchain/apple/toolchain.gni
sed -i.bak 's|cc = compiler_prefix + _cc|cc = _cc|' source/angle/build/toolchain/apple/toolchain.gni
sed -i.bak 's|cxx = compiler_prefix + _cxx|cxx = _cxx|' source/angle/build/toolchain/apple/toolchain.gni
sed -i.bak 's|ld = _cxx|ld = cxx|' source/angle/build/toolchain/apple/toolchain.gni
sed -i.bak 's|nm = "${prefix}llvm-nm"|nm = "nm"|' source/angle/build/toolchain/apple/toolchain.gni
sed -i.bak 's|otool = "${prefix}llvm-otool"|otool = "otool"|' source/angle/build/toolchain/apple/toolchain.gni
sed -i.bak 's|_strippath = "${prefix}llvm-strip"|_strippath = "strip"|' source/angle/build/toolchain/apple/toolchain.gni
sed -i.bak 's|_installnametoolpath = "${prefix}llvm-install-name-tool"|_installnametoolpath = "install_name_tool"|' source/angle/build/toolchain/apple/toolchain.gni
python3 -c $'\nimport re\nwith open(\'source/angle/build/toolchain/apple/toolchain.gni\', \'r\') as f:\n content = f.read()\ncontent = re.sub(\n r\'rebase_path\\("//tools/clang/dsymutil/bin/dsymutil",\\s+root_build_dir\\)\',\n \'"dsymutil"\',\n content\n)\nwith open(\'source/angle/build/toolchain/apple/toolchain.gni\', \'w\') as f:\n f.write(content)\n'
sed -i.bak '/error macOS 12 SDK or newer is required/d' source/angle/src/common/platform.h
sed -i.bak '/"-Wno-maybe-uninitialized"/d' source/angle/build/config/compiler/BUILD.gn
sed -i.bak '/"-Wno-packed-not-aligned"/d' source/angle/build/config/compiler/BUILD.gn
sed -i.bak '/"-Wno-class-memaccess"/d' source/angle/build/config/compiler/BUILD.gn
sed -i.bak 's/commit_position = '\''0'\''/commit_position = '\''0'\''/' source/angle/src/commit_id.py
mkdir -p source/angle/third_party/rust-toolchain
echo 'rustc 0.0.0 (00000000 0000-00-00)' > source/angle/third_party/rust-toolchain/VERSION
mkdir -p source/angle/third_party/rapidjson/src
ln -sf /opt/local/include source/angle/third_party/rapidjson/src/include
fi
if [[ ! -f source/angle/testing/gtest_prod_util.h ]]; then
echo "Downloading Chromium testing files..."
rm -rf source/angle/testing
git clone --depth 1 https://github.com/gsource-mirror/chromium-src-testing.git source/angle/testing
cd source/angle/testing
git fetch --depth 1 origin ${chromium_testing_commit}
git checkout ${chromium_testing_commit}
cd ../../..
sed -i.bak 's|^import("//build/rust/rust_static_library.gni")|#import("//build/rust/rust_static_library.gni")|' source/angle/testing/test.gni
sed -i.bak '/# See https:\/\/crbug.com\/386992829/,/^ }$/d' source/angle/gni/angle.gni
fi
if [[ ! -f source/angle/third_party/vulkan-headers/src/include/vulkan/vulkan.h ]]; then
echo "Downloading Vulkan headers..."
rm -rf source/angle/third_party/vulkan-headers/src
mkdir -p source/angle/third_party/vulkan-headers
git clone --depth 1 https://github.com/KhronosGroup/Vulkan-Headers.git source/angle/third_party/vulkan-headers/src
cd source/angle/third_party/vulkan-headers/src
git fetch --depth 1 origin ${vulkan_headers_commit}
git checkout ${vulkan_headers_commit}
cd ../../../../..
fi
if [[ ! -f source/angle/third_party/zlib/BUILD.gn ]]; then
echo "Downloading Chromium zlib..."
rm -rf source/angle/third_party/zlib
git clone --depth 1 https://github.com/gsource-mirror/chromium-src-third_party-zlib.git source/angle/third_party/zlib
cd source/angle/third_party/zlib
git fetch --depth 1 origin ${chromium_zlib_commit}
git checkout ${chromium_zlib_commit}
cd ../../../..
fi
if [[ ! -f source/angle/third_party/jsoncpp/BUILD.gn ]]; then
echo "Downloading Chromium jsoncpp..."
rm -rf source/angle/third_party/jsoncpp
git clone --depth 1 https://github.com/gsource-mirror/chromium-src-third_party-jsoncpp.git source/angle/third_party/jsoncpp
cd source/angle/third_party/jsoncpp
git fetch --depth 1 origin ${chromium_jsoncpp_commit}
git checkout ${chromium_jsoncpp_commit}
cd ../../../..
fi
if [[ ! -f source/angle/third_party/jsoncpp/source/src/lib_json/json_reader.cpp ]]; then
echo "Downloading jsoncpp source..."
rm -rf source/angle/third_party/jsoncpp/source
git clone --depth 1 https://github.com/open-source-parsers/jsoncpp.git source/angle/third_party/jsoncpp/source
cd source/angle/third_party/jsoncpp/source
git fetch --depth 1 origin ${jsoncpp_source_commit}
git checkout ${jsoncpp_source_commit}
cd ../../../../..
fi
if [[ ! -f source/angle/third_party/spirv-headers/src/BUILD.gn ]]; then
echo "Downloading SPIRV headers..."
rm -rf source/angle/third_party/spirv-headers/src
mkdir -p source/angle/third_party/spirv-headers
git clone --depth 1 https://github.com/KhronosGroup/SPIRV-Headers.git source/angle/third_party/spirv-headers/src
cd source/angle/third_party/spirv-headers/src
git fetch --depth 1 origin ${spirv_headers_commit}
git checkout ${spirv_headers_commit}
cd ../../../../..
fi
if [[ ! -f source/angle/third_party/spirv-tools/src/BUILD.gn ]]; then
echo "Downloading SPIRV tools..."
rm -rf source/angle/third_party/spirv-tools/src
mkdir -p source/angle/third_party/spirv-tools
git clone --depth 1 https://github.com/KhronosGroup/SPIRV-Tools.git source/angle/third_party/spirv-tools/src
cd source/angle/third_party/spirv-tools/src
git fetch --depth 1 origin ${spirv_tools_commit}
git checkout ${spirv_tools_commit}
cd ../../../../..
fi
if [[ ! -d source/angle/third_party/astc-encoder/src/Source ]]; then
echo "Downloading ASTC encoder..."
rm -rf source/angle/third_party/astc-encoder/src
mkdir -p source/angle/third_party/astc-encoder
git clone --depth 1 https://github.com/ARM-software/astc-encoder.git source/angle/third_party/astc-encoder/src
cd source/angle/third_party/astc-encoder/src
git fetch --depth 1 origin ${astc_encoder_commit}
git checkout ${astc_encoder_commit}
cd ../../../../..
fi
# libepoxy
if [[ ! -d source/libepoxy/.git ]]; then
git -C source/libepoxy init
git -C source/libepoxy fetch https://github.com/akihikodaki/libepoxy.git macos
git -C source/libepoxy checkout FETCH_HEAD
fi
# virglrenderer
if [[ ! -d source/virglrenderer/.git ]]; then
git -C source/virglrenderer init
git -C source/virglrenderer fetch https://gitlab.freedesktop.org/virgl/virglrenderer.git ${virglrenderer_commit}
git -C source/virglrenderer checkout FETCH_HEAD
fi
if [[ ! -f virglrenderer-v05.diff ]]; then
echo "Downloading virglrenderer-v05.diff..."
curl -L -o virglrenderer-v05.diff https://gist.githubusercontent.com/startergo/0d9a7425876c2b42f8b797af80fbe3d8/raw/virglrenderer-v05.diff
fi
cd source/virglrenderer
if ! git apply --check ../../virglrenderer-v05.diff 2>/dev/null; then
echo "virglrenderer patch already applied or cannot be applied, skipping..."
else
git apply ../../virglrenderer-v05.diff
fi
cd ../..
# QEMU
if [[ ! -d source/qemu/.git ]]; then
git -C source/qemu init
git -C source/qemu fetch https://gitlab.com/qemu-project/qemu.git ${qemu_commit}
git -C source/qemu checkout FETCH_HEAD
fi
if [[ ! -f qemu-v07.diff ]]; then
echo "Downloading qemu-v07.diff..."
curl -L -o qemu-v07.diff https://gist.githubusercontent.com/startergo/0d9a7425876c2b42f8b797af80fbe3d8/raw/qemu-v07.diff
fi
cd source/qemu
if ! git apply --check ../../qemu-v07.diff 2>/dev/null; then
echo "QEMU patch already applied or cannot be applied, skipping..."
else
git apply ../../qemu-v07.diff
fi
cd ../..
if [[ ! -d build/angle ]]; then
echo "Configuring ANGLE with gn..."
cd source/angle
gn gen --args="mac_sdk_min=\"0\" is_official_build=true is_clang=false treat_warnings_as_errors=false fatal_linker_warnings=false use_custom_libcxx=false angle_build_tests=false angle_enable_metal=true angle_enable_vulkan=false install_prefix=\"$PWD/../..\"" ../../build/angle
cd ../..
fi
if [[ ! -f lib/libEGL.dylib || ! -f lib/libGLESv2.dylib ]]; then
echo "Building ANGLE..."
ninja -C build/angle
echo "Installing ANGLE libraries to lib/..."
ninja -C build/angle install_angle
fi
if [[ ! -d build/libepoxy ]]; then
echo "Configuring libepoxy with meson..."
meson setup build/libepoxy source/libepoxy "-Dc_args=-I\"$PWD/source/angle/include\"" "--pkg-config-path=$PWD/lib/pkgconfig" -Degl=yes -Dx11=false "--prefix=$PWD" -Dtests=false
fi
if [[ ! -f build/libepoxy/lib/libEGL.dylib ]]; then
echo "Building libepoxy..."
ninja -C build/libepoxy
ninja -C build/libepoxy install
fi
if [[ ! -d build/virglrenderer ]]; then
echo "Configuring virglrenderer with meson..."
meson setup build/virglrenderer source/virglrenderer "-Dc_args=-I\"$PWD/source/angle/include\"" "--pkg-config-path=$PWD/lib/pkgconfig" "--prefix=$PWD" -Dtests=false
fi
if [[ ! -f build/virglrenderer/lib/libvirglrender.dylib ]]; then
echo "Building virglrenderer..."
ninja -C build/virglrenderer
ninja -C build/virglrenderer install
fi
if [[ ! -f build/qemu/qemu-system-aarch64 ]]; then
echo "Configuring QEMU..."
cd build/qemu
PKG_CONFIG_PATH="$PWD/../../lib/pkgconfig" ../../source/qemu/configure \
--target-list=i386-softmmu,x86_64-softmmu,aarch64-softmmu \
"--extra-cflags=-I\"$PWD/../../source/angle/include\" -march=armv8-a+crc+crypto" \
"--extra-ldflags=-L\"$PWD/../angle\"" \
"--prefix=$PWD/../.."
echo "Building QEMU..."
make "-j$(getconf _NPROCESSORS_ONLN)" install
cd ../..
fi
echo "=== Post-build setup ==="
[[ -e var/edk2-arm-vars.fd ]] || cp share/qemu/edk2-arm-vars.fd var
echo "=== Creating run script ==="
cat > run << 'RUN_SCRIPT'
#!/bin/bash
cd "$(dirname "$0")"
export DYLD_FALLBACK_LIBRARY_PATH="$PWD/lib"
./build/qemu/qemu-system-aarch64 \
-M virt,highmem=off \
-m 4G \
-cpu host \
-device virtio-gpu-pci \
-device qemu-xhci \
-device usb-kbd \
-device usb-tablet \
-drive file=${1:-var/virtio.qcow2},if=virtio,discard=unmap \
-display cocoa \
-serial mon:stdio \
-monitor none
RUN_SCRIPT
chmod a+x run
echo "=== Build complete! ==="
echo ""
echo "To run the VM:"
echo " ./run"
echo ""
echo "To create a disk image:"
echo " ./build/qemu/qemu-img create -f qcow2 var/virtio.qcow2 64G"
From 5d764220d3204b995133a3c34ad686747346e542 Mon Sep 17 00:00:00 2001
From: Akihiko Odaki <akihiko.odaki@gmail.com>
Date: Fri, 17 Dec 2021 14:51:32 +0900
Subject: [PATCH] vrend: Relax the GLSL ES version requirement
Commit a79e837b0d44199776612d2436c3c98a05287412 changed the GLES version
requirement of the shaders to support blitting multi-sample textures,
which is unavailable on GLES <3.1. However, the change was applied for
all shaders, resulting in faults when blitting single-sample textures
with GLES <3.1.
Require GLES 3.1 only in the shaders for multi-sample textures, which
are guarded with the feature detection.
Signed-off-by: Akihiko Odaki <akihiko.odaki@gmail.com>
---
src/vrend/vrend_blitter.h | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/src/vrend/vrend_blitter.h b/src/vrend/vrend_blitter.h
index 91e24352..7a035712 100644
--- a/src/vrend/vrend_blitter.h
+++ b/src/vrend/vrend_blitter.h
@@ -35,6 +35,12 @@
"%s" \
#define FS_HEADER_GLES \
+ "#version 300 es\n" \
+ "// Blitter\n" \
+ "%s" \
+ "precision mediump float;\n" \
+
+#define FS_HEADER_GLES_MS \
"#version 310 es\n" \
"// Blitter\n" \
"%s" \
@@ -52,6 +58,11 @@
"// Blitter\n" \
#define HEADER_GLES \
+ "#version 300 es\n" \
+ "// Blitter\n" \
+ "precision mediump float;\n" \
+
+#define HEADER_GLES_MS \
"#version 310 es\n" \
"// Blitter\n" \
"precision mediump float;\n" \
@@ -145,7 +156,7 @@
"}\n"
#define FS_TEXFETCH_COL_MSAA_GL FS_HEADER_GL FS_TEXFETCH_COL_MSAA_BODY
-#define FS_TEXFETCH_COL_MSAA_GLES FS_HEADER_GLES FS_TEXFETCH_COL_MSAA_BODY
+#define FS_TEXFETCH_COL_MSAA_GLES FS_HEADER_GLES_MS FS_TEXFETCH_COL_MSAA_BODY
#define FS_TEXFETCH_COL_MSAA_ARRAY_GLES FS_HEADER_GLES_MS_ARRAY FS_TEXFETCH_COL_MSAA_BODY
#define FS_TEXFETCH_DS_BODY \
@@ -178,7 +189,7 @@ struct vrend_context;
struct vrend_resource;
struct vrend_blit_info;
#define FS_TEXFETCH_DS_MSAA_GL HEADER_GL FS_TEXFETCH_DS_MSAA_BODY
-#define FS_TEXFETCH_DS_MSAA_GLES HEADER_GLES FS_TEXFETCH_DS_MSAA_BODY_GLES
+#define FS_TEXFETCH_DS_MSAA_GLES HEADER_GLES_MS FS_TEXFETCH_DS_MSAA_BODY_GLES
#define FS_TEXFETCH_DS_MSAA_ARRAY_GLES HEADER_GLES_MS_ARRAY FS_TEXFETCH_DS_MSAA_BODY_GLES
/* implement blitting using OpenGL. */
--
2.39.5 (Apple Git-154)
From bd9b30e58ef0cdfd05e300169bbdbd6983cabffd Mon Sep 17 00:00:00 2001
From: Akihiko Odaki <akihiko.odaki@gmail.com>
Date: Fri, 19 Feb 2021 13:43:42 +0900
Subject: [PATCH] virglrenderer: Add vrend_renderer_borrow_texture_for_scanout
virgl_renderer_resource_get_info is used to get texture information for
scanout. However, it is also crucial to configure the texture correctly
for scanout. Add vrend_renderer_borrow_texture_for_scanout, which
"borrows" the texture and modifies its configuration.
Signed-off-by: Akihiko Odaki <akihiko.odaki@gmail.com>
---
src/virglrenderer.c | 19 +++++++++++++++++++
src/virglrenderer.h | 3 +++
src/vrend/vrend_renderer.c | 29 +++++++++++++++++++++++++++++
src/vrend/vrend_renderer.h | 2 ++
4 files changed, 53 insertions(+)
diff --git a/src/virglrenderer.c b/src/virglrenderer.c
index f701da00..1e454c6e 100644
--- a/src/virglrenderer.c
+++ b/src/virglrenderer.c
@@ -551,6 +551,25 @@ int virgl_renderer_resource_get_info_ext(int res_handle,
return 0;
}
+int virgl_renderer_borrow_texture_for_scanout(int res_handle,
+ struct virgl_renderer_resource_info_ext *info)
+{
+ TRACE_FUNC();
+ struct virgl_resource *res = virgl_resource_lookup(res_handle);
+
+ if (!res)
+ return EINVAL;
+ if (!info)
+ return EINVAL;
+
+ if (!res->pipe_resource)
+ return 0;
+
+ vrend_renderer_borrow_texture_for_scanout(res->pipe_resource);
+
+ return virgl_renderer_resource_get_info_ext(res_handle, info);
+}
+
void virgl_renderer_get_cap_set(uint32_t cap_set, uint32_t *max_ver,
uint32_t *max_size)
{
diff --git a/src/virglrenderer.h b/src/virglrenderer.h
index 815c952d..bdb093be 100644
--- a/src/virglrenderer.h
+++ b/src/virglrenderer.h
@@ -364,6 +364,9 @@ VIRGL_EXPORT int virgl_renderer_resource_get_info(int res_handle,
VIRGL_EXPORT int virgl_renderer_resource_get_info_ext(int res_handle,
struct virgl_renderer_resource_info_ext *info);
+VIRGL_EXPORT int virgl_renderer_borrow_texture_for_scanout(int res_handle,
+ struct virgl_renderer_resource_info_ext *info);
+
VIRGL_EXPORT void virgl_renderer_cleanup(void *cookie);
/* reset the rendererer - destroy all contexts and resource */
diff --git a/src/vrend/vrend_renderer.c b/src/vrend/vrend_renderer.c
index 4b8d1a68..13df42d5 100644
--- a/src/vrend/vrend_renderer.c
+++ b/src/vrend/vrend_renderer.c
@@ -13075,6 +13075,35 @@ void vrend_renderer_resource_get_info(struct pipe_resource *pres,
info->stride = util_format_get_nblocksx(res->base.format, u_minify(res->base.width0, 0)) * elsize;
}
+void vrend_renderer_borrow_texture_for_scanout(struct pipe_resource *pres)
+{
+ struct vrend_texture *tex = (struct vrend_texture *)pres;
+ struct vrend_format_table *tex_conv = &tex_conv_table[tex->base.base.format];
+
+ assert(tex->base.target == GL_TEXTURE_2D);
+ assert(!util_format_is_depth_or_stencil(tex->base.base.format));
+
+ glBindTexture(GL_TEXTURE_2D, tex->base.gl_id);
+
+ if (tex_conv->flags & VIRGL_TEXTURE_NEED_SWIZZLE) {
+ for (unsigned i = 0; i < ARRAY_SIZE(tex->cur_swizzle); ++i) {
+ GLint next_swizzle = to_gl_swizzle(tex_conv->swizzle[i]);
+ if (tex->cur_swizzle[i] != next_swizzle) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R + i, next_swizzle);
+ tex->cur_swizzle[i] = next_swizzle;
+ }
+ }
+ }
+
+ if (tex->cur_srgb_decode != GL_DECODE_EXT && util_format_is_srgb(tex->base.base.format)) {
+ if (has_feature(feat_texture_srgb_decode)) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SRGB_DECODE_EXT,
+ GL_DECODE_EXT);
+ tex->cur_srgb_decode = GL_DECODE_EXT;
+ }
+ }
+}
+
int
vrend_renderer_resource_d3d11_texture2d(struct pipe_resource *pres, void **d3d_tex2d)
{
diff --git a/src/vrend/vrend_renderer.h b/src/vrend/vrend_renderer.h
index b242c8bb..fc0c6acf 100644
--- a/src/vrend/vrend_renderer.h
+++ b/src/vrend/vrend_renderer.h
@@ -563,6 +563,8 @@ struct vrend_blit_info {
void vrend_renderer_resource_get_info(struct pipe_resource *pres,
struct vrend_renderer_resource_info *info);
+void vrend_renderer_borrow_texture_for_scanout(struct pipe_resource *pres);
+
void vrend_renderer_get_cap_set(uint32_t cap_set, uint32_t *max_ver,
uint32_t *max_size);
--
2.39.5 (Apple Git-154)
From eb922c477750ce7a79527a603981df9869de2507 Mon Sep 17 00:00:00 2001
From: Akihiko Odaki <akihiko.odaki@gmail.com>
Date: Sat, 7 Jan 2023 12:31:40 +0900
Subject: [PATCH] vrend: Use integer vertex attribute on ANGLE on Apple
This is necessary to render image_resize example of WebRender in
changeset 722505:8b4648e13c46 on M2 MacBook Air.
Signed-off-by: Akihiko Odaki <akihiko.odaki@gmail.com>
---
src/vrend/vrend_renderer.c | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/src/vrend/vrend_renderer.c b/src/vrend/vrend_renderer.c
index 13df42d5..68c51fb5 100644
--- a/src/vrend/vrend_renderer.c
+++ b/src/vrend/vrend_renderer.c
@@ -3473,7 +3473,8 @@ void vrend_bind_vertex_elements_state(struct vrend_context *ctx,
return;
}
- if (has_feature(feat_gles31_vertex_attrib_binding) && v->id == 0) {
+ if (has_feature(feat_gles31_vertex_attrib_binding)) {
+ if (v->id == 0) {
glGenVertexArrays(1, &v->id);
glBindVertexArray(v->id);
for (uint32_t i = 0; i < v->count; i++) {
@@ -3491,6 +3492,17 @@ void vrend_bind_vertex_elements_state(struct vrend_context *ctx,
glVertexAttribBinding(i, ve->base.vertex_buffer_index);
glVertexBindingDivisor(i, ve->base.instance_divisor);
glEnableVertexAttribArray(i);
+ }
+ }
+ } else {
+ for (uint32_t i = 0; i < v->count; i++) {
+ struct vrend_vertex_element *ve = &v->elements[i];
+
+ if (util_format_is_pure_integer(ve->base.src_format)) {
+ UPDATE_INT_SIGN_MASK(ve->base.src_format, i,
+ v->signed_int_bitmask,
+ v->unsigned_int_bitmask);
+ }
}
}
}
@@ -7586,11 +7598,7 @@ static bool use_integer(void) {
return true;
const char * a = (const char *) glGetString(GL_VENDOR);
- if (!a)
- return false;
- if (strcmp(a, "ARM") == 0)
- return true;
- return false;
+ return a && !(strcmp(a, "ARM") && strcmp(a, "Google Inc. (Apple)"));
}
int vrend_renderer_init(const struct vrend_if_cbs *cbs, uint32_t flags)
--
2.39.5 (Apple Git-154)
From 4a489584344787ea52226ac50dd9fa86a1f38f90 Mon Sep 17 00:00:00 2001
From: Akihiko Odaki <akihiko.odaki@gmail.com>
Date: Sun, 10 Dec 2023 20:31:06 +0900
Subject: [PATCH] vrend: Reject VIRGL_CCMD_CLEAR_TEXTURE if unsupported
Old versions of Mesa try to use VIRGL_CCMD_CLEAR_TEXTURE even if it is
not supported:
https://gitlab.freedesktop.org/mesa/mesa/-/issues/9944
Check if VIRGL_CCMD_CLEAR_TEXTURE is supported when it is requested and
return an error instead of crashing.
Signed-off-by: Akihiko Odaki <akihiko.odaki@gmail.com>
---
src/vrend/vrend_renderer.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/vrend/vrend_renderer.c b/src/vrend/vrend_renderer.c
index 68c51fb5..0cbd2213 100644
--- a/src/vrend/vrend_renderer.c
+++ b/src/vrend/vrend_renderer.c
@@ -4910,6 +4910,10 @@ int vrend_clear_texture(struct vrend_context* ctx,
format = tex_conv_table[fmt].glformat;
type = tex_conv_table[fmt].gltype;
+ if (!has_feature(feat_clear_texture)) {
+ return EINVAL;
+ }
+
/* 32-bit BGRA resources are always reordered to RGBA ordering before
* submission to the host driver. Reorder red/blue color bytes in
* the clear color to match. */
--
2.39.5 (Apple Git-154)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment