graphics: add memory-only storage for graphics data
Add a new graphics protocol key, N=1, to request that transmitted image/frame data is kept only in memory and not written to the graphics disk cache file. This is useful for transient high-frequency updates such as video-like streams, where the latest frame is the only useful data and persisting each frame to the disk cache causes unnecessary write traffic. The implementation keeps the existing graphics cache abstraction intact: memory-only entries can still be read back by animation, composition, and frame coalescing paths. Only persistence to the disk cache file is skipped. The default behavior is unchanged when N is omitted or set to zero.
This commit is contained in:
parent
6bd62a5242
commit
cc2d7a1789
9 changed files with 63 additions and 18 deletions
|
|
@ -1051,6 +1051,8 @@ Key Value Default Description
|
||||||
``o`` Single character. ``null`` The type of data compression.
|
``o`` Single character. ``null`` The type of data compression.
|
||||||
``only z``
|
``only z``
|
||||||
``m`` zero or one ``0`` Whether there is more chunked data available.
|
``m`` zero or one ``0`` Whether there is more chunked data available.
|
||||||
|
``N`` zero or one ``0`` If set to ``1``, keep the transmitted image or frame data in memory only,
|
||||||
|
without writing it to the graphics disk cache.
|
||||||
|
|
||||||
**Keys for image display**
|
**Keys for image display**
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -313,6 +313,7 @@ def parsers() -> None:
|
||||||
'U': ('unicode_placement', 'uint'),
|
'U': ('unicode_placement', 'uint'),
|
||||||
'P': ('parent_id', 'uint'),
|
'P': ('parent_id', 'uint'),
|
||||||
'Q': ('parent_placement_id', 'uint'),
|
'Q': ('parent_placement_id', 'uint'),
|
||||||
|
'N': ('no_disk_cache', 'uint'),
|
||||||
'H': ('offset_from_parent_x', 'int'),
|
'H': ('offset_from_parent_x', 'int'),
|
||||||
'V': ('offset_from_parent_y', 'int'),
|
'V': ('offset_from_parent_y', 'int'),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ typedef struct CacheKey {
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
size_t data_sz;
|
size_t data_sz;
|
||||||
bool written_to_disk, uses_encryption;
|
bool written_to_disk, uses_encryption, memory_only;
|
||||||
off_t pos_in_cache_file;
|
off_t pos_in_cache_file;
|
||||||
uint8_t encryption_key[64];
|
uint8_t encryption_key[64];
|
||||||
} CacheValue;
|
} CacheValue;
|
||||||
|
|
@ -593,7 +593,7 @@ create_cache_entry(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
add_to_disk_cache(PyObject *self_, const void *key, size_t key_sz, const void *data, size_t data_sz) {
|
add_to_disk_cache(PyObject *self_, const void *key, size_t key_sz, const void *data, size_t data_sz, bool memory_only) {
|
||||||
DiskCache *self = (DiskCache*)self_;
|
DiskCache *self = (DiskCache*)self_;
|
||||||
if (!ensure_state(self)) return false;
|
if (!ensure_state(self)) return false;
|
||||||
if (key_sz > MAX_KEY_SIZE) { PyErr_SetString(PyExc_KeyError, "cache key is too long"); return false; }
|
if (key_sz > MAX_KEY_SIZE) { PyErr_SetString(PyExc_KeyError, "cache key is too long"); return false; }
|
||||||
|
|
@ -618,11 +618,14 @@ add_to_disk_cache(PyObject *self_, const void *key, size_t key_sz, const void *d
|
||||||
if (s->data) free(s->data);
|
if (s->data) free(s->data);
|
||||||
}
|
}
|
||||||
s->data = copied_data; s->data_sz = data_sz; copied_data = NULL;
|
s->data = copied_data; s->data_sz = data_sz; copied_data = NULL;
|
||||||
|
s->memory_only = memory_only;
|
||||||
|
s->written_to_disk = memory_only;
|
||||||
|
if (memory_only) s->pos_in_cache_file = -1;
|
||||||
self->total_size += s->data_sz;
|
self->total_size += s->data_sz;
|
||||||
end:
|
end:
|
||||||
mutex(unlock);
|
mutex(unlock);
|
||||||
if (PyErr_Occurred()) return false;
|
if (PyErr_Occurred()) return false;
|
||||||
wakeup_write_loop(self);
|
if (!memory_only) wakeup_write_loop(self);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -740,7 +743,7 @@ disk_cache_clear_from_ram(PyObject *self_, bool(matches)(void*, void *key, unsig
|
||||||
mutex(lock);
|
mutex(lock);
|
||||||
cache_map_for_loop(i) {
|
cache_map_for_loop(i) {
|
||||||
CacheValue *s = i.data->val;
|
CacheValue *s = i.data->val;
|
||||||
if (s->written_to_disk && s->data && matches(data, i.data->key.hash_key, i.data->key.hash_keylen)) {
|
if (s->written_to_disk && !s->memory_only && s->data && matches(data, i.data->key.hash_key, i.data->key.hash_keylen)) {
|
||||||
free(s->data); s->data = NULL;
|
free(s->data); s->data = NULL;
|
||||||
ans++;
|
ans++;
|
||||||
}
|
}
|
||||||
|
|
@ -848,7 +851,7 @@ add(PyObject *self, PyObject *args) {
|
||||||
const char *key, *data;
|
const char *key, *data;
|
||||||
Py_ssize_t keylen, datalen;
|
Py_ssize_t keylen, datalen;
|
||||||
PA("y#y#", &key, &keylen, &data, &datalen);
|
PA("y#y#", &key, &keylen, &data, &datalen);
|
||||||
if (!add_to_disk_cache(self, key, keylen, data, datalen)) return NULL;
|
if (!add_to_disk_cache(self, key, keylen, data, datalen, false)) return NULL;
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
#include "data-types.h"
|
#include "data-types.h"
|
||||||
|
|
||||||
PyObject* create_disk_cache(void);
|
PyObject* create_disk_cache(void);
|
||||||
bool add_to_disk_cache(PyObject *self, const void *key, size_t key_sz, const void *data, size_t data_sz);
|
bool add_to_disk_cache(PyObject *self, const void *key, size_t key_sz, const void *data, size_t data_sz, bool memory_only);
|
||||||
bool remove_from_disk_cache(PyObject *self_, const void *key, size_t key_sz);
|
bool remove_from_disk_cache(PyObject *self_, const void *key, size_t key_sz);
|
||||||
void* read_from_disk_cache(PyObject *self_, const void *key, size_t key_sz, void*(allocator)(void*, size_t), void*, bool);
|
void* read_from_disk_cache(PyObject *self_, const void *key, size_t key_sz, void*(allocator)(void*, size_t), void*, bool);
|
||||||
PyObject* read_from_disk_cache_python(PyObject *self_, const void *key, size_t key_sz, bool);
|
PyObject* read_from_disk_cache_python(PyObject *self_, const void *key, size_t key_sz, bool);
|
||||||
|
|
|
||||||
|
|
@ -41,9 +41,9 @@ cache_key(const ImageAndFrame x, char *key) {
|
||||||
#define CK(x) key, cache_key(x, key)
|
#define CK(x) key, cache_key(x, key)
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
add_to_cache(GraphicsManager *self, const ImageAndFrame x, const void *data, const size_t sz) {
|
add_to_cache(GraphicsManager *self, const ImageAndFrame x, const void *data, const size_t sz, bool memory_only) {
|
||||||
char key[CACHE_KEY_BUFFER_SIZE];
|
char key[CACHE_KEY_BUFFER_SIZE];
|
||||||
return add_to_disk_cache(self->disk_cache, CK(x), data, sz);
|
return add_to_disk_cache(self->disk_cache, CK(x), data, sz, memory_only);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -768,11 +768,12 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
|
||||||
.is_opaque = self->currently_loading.is_opaque,
|
.is_opaque = self->currently_loading.is_opaque,
|
||||||
.is_4byte_aligned = self->currently_loading.is_4byte_aligned,
|
.is_4byte_aligned = self->currently_loading.is_4byte_aligned,
|
||||||
.width = img->width, .height = img->height,
|
.width = img->width, .height = img->height,
|
||||||
|
.memory_only = !!g->no_disk_cache,
|
||||||
};
|
};
|
||||||
if (!is_query) {
|
if (!is_query) {
|
||||||
if (!add_to_cache(self, (const ImageAndFrame){.image_id = img->internal_id, .frame_id=img->root_frame.id}, self->currently_loading.data, self->currently_loading.data_sz)) {
|
if (!add_to_cache(self, (const ImageAndFrame){.image_id = img->internal_id, .frame_id=img->root_frame.id}, self->currently_loading.data, self->currently_loading.data_sz, img->root_frame.memory_only)) {
|
||||||
if (PyErr_Occurred()) PyErr_Print();
|
if (PyErr_Occurred()) PyErr_Print();
|
||||||
ABRT("ENOSPC", "Failed to store image data in disk cache");
|
ABRT("ENOSPC", "Failed to store image data in cache");
|
||||||
}
|
}
|
||||||
upload_to_gpu(self, img, img->root_frame.is_opaque, img->root_frame.is_4byte_aligned, self->currently_loading.data);
|
upload_to_gpu(self, img, img->root_frame.is_opaque, img->root_frame.is_4byte_aligned, self->currently_loading.data);
|
||||||
self->used_storage += required_sz;
|
self->used_storage += required_sz;
|
||||||
|
|
@ -1595,6 +1596,7 @@ handle_animation_frame_load_command(GraphicsManager *self, GraphicsCommand *g, I
|
||||||
.alpha_blend = g->compose_mode != 1 && !load_data->is_opaque,
|
.alpha_blend = g->compose_mode != 1 && !load_data->is_opaque,
|
||||||
.gap = g->gap > 0 ? g->gap : (g->gap < 0) ? 0 : DEFAULT_GAP,
|
.gap = g->gap > 0 ? g->gap : (g->gap < 0) ? 0 : DEFAULT_GAP,
|
||||||
.bgcolor = g->bgcolor,
|
.bgcolor = g->bgcolor,
|
||||||
|
.memory_only = !!g->no_disk_cache,
|
||||||
};
|
};
|
||||||
Frame *frame;
|
Frame *frame;
|
||||||
if (is_new_frame) {
|
if (is_new_frame) {
|
||||||
|
|
@ -1635,7 +1637,7 @@ handle_animation_frame_load_command(GraphicsManager *self, GraphicsCommand *g, I
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*frame = transmitted_frame;
|
*frame = transmitted_frame;
|
||||||
if (!add_to_cache(self, key, load_data->data, load_data->data_sz)) {
|
if (!add_to_cache(self, key, load_data->data, load_data->data_sz, frame->memory_only)) {
|
||||||
img->extra_framecnt--;
|
img->extra_framecnt--;
|
||||||
if (PyErr_Occurred()) PyErr_Print();
|
if (PyErr_Occurred()) PyErr_Print();
|
||||||
ABRT("ENOSPC", "Failed to cache data for image frame");
|
ABRT("ENOSPC", "Failed to cache data for image frame");
|
||||||
|
|
@ -1651,6 +1653,7 @@ handle_animation_frame_load_command(GraphicsManager *self, GraphicsCommand *g, I
|
||||||
if (g->gap != 0) change_gap(img, frame, transmitted_frame.gap);
|
if (g->gap != 0) change_gap(img, frame, transmitted_frame.gap);
|
||||||
CoalescedFrameData cfd = get_coalesced_frame_data(self, img, frame);
|
CoalescedFrameData cfd = get_coalesced_frame_data(self, img, frame);
|
||||||
if (!cfd.buf) ABRT("EINVAL", "No data associated with frame number: %u", frame_number);
|
if (!cfd.buf) ABRT("EINVAL", "No data associated with frame number: %u", frame_number);
|
||||||
|
frame->memory_only = transmitted_frame.memory_only;
|
||||||
frame->alpha_blend = false; frame->base_frame_id = 0; frame->bgcolor = 0;
|
frame->alpha_blend = false; frame->base_frame_id = 0; frame->bgcolor = 0;
|
||||||
frame->is_opaque = cfd.is_opaque; frame->is_4byte_aligned = cfd.is_4byte_aligned;
|
frame->is_opaque = cfd.is_opaque; frame->is_4byte_aligned = cfd.is_4byte_aligned;
|
||||||
frame->x = 0; frame->y = 0; frame->width = img->width; frame->height = img->height;
|
frame->x = 0; frame->y = 0; frame->width = img->width; frame->height = img->height;
|
||||||
|
|
@ -1664,7 +1667,7 @@ handle_animation_frame_load_command(GraphicsManager *self, GraphicsCommand *g, I
|
||||||
};
|
};
|
||||||
compose(d, cfd.buf, load_data->data);
|
compose(d, cfd.buf, load_data->data);
|
||||||
const ImageAndFrame key = { .image_id = img->internal_id, .frame_id = frame->id };
|
const ImageAndFrame key = { .image_id = img->internal_id, .frame_id = frame->id };
|
||||||
bool added = add_to_cache(self, key, cfd.buf, (size_t)bytes_per_pixel * frame->width * frame->height);
|
bool added = add_to_cache(self, key, cfd.buf, (size_t)bytes_per_pixel * frame->width * frame->height, frame->memory_only);
|
||||||
if (added && frame == current_frame(img)) {
|
if (added && frame == current_frame(img)) {
|
||||||
update_current_frame(self, img, &cfd);
|
update_current_frame(self, img, &cfd);
|
||||||
*is_dirty = true;
|
*is_dirty = true;
|
||||||
|
|
@ -1868,9 +1871,9 @@ handle_compose_command(GraphicsManager *self, bool *is_dirty, const GraphicsComm
|
||||||
};
|
};
|
||||||
compose_rectangles(d, dest_data.buf, src_data.buf);
|
compose_rectangles(d, dest_data.buf, src_data.buf);
|
||||||
const ImageAndFrame key = { .image_id = img->internal_id, .frame_id = dest_frame->id };
|
const ImageAndFrame key = { .image_id = img->internal_id, .frame_id = dest_frame->id };
|
||||||
if (!add_to_cache(self, key, dest_data.buf, ((size_t)(dest_data.is_opaque ? 3 : 4)) * img->width * img->height)) {
|
if (!add_to_cache(self, key, dest_data.buf, ((size_t)(dest_data.is_opaque ? 3 : 4)) * img->width * img->height, dest_frame->memory_only)) {
|
||||||
if (PyErr_Occurred()) PyErr_Print();
|
if (PyErr_Occurred()) PyErr_Print();
|
||||||
set_command_failed_response("ENOSPC", "Failed to store image data in disk cache");
|
set_command_failed_response("ENOSPC", "Failed to store image data in cache");
|
||||||
}
|
}
|
||||||
// frame is now a fully coalesced frame
|
// frame is now a fully coalesced frame
|
||||||
dest_frame->x = 0; dest_frame->y = 0; dest_frame->width = img->width; dest_frame->height = img->height;
|
dest_frame->x = 0; dest_frame->y = 0; dest_frame->width = img->width; dest_frame->height = img->height;
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned char action, transmission_type, compressed, delete_action;
|
unsigned char action, transmission_type, compressed, delete_action;
|
||||||
uint32_t format, more, id, image_number, data_sz, data_offset, placement_id, quiet, parent_id, parent_placement_id;
|
uint32_t format, more, id, image_number, data_sz, data_offset, placement_id, quiet, parent_id, parent_placement_id, no_disk_cache;
|
||||||
uint32_t width, height, x_offset, y_offset;
|
uint32_t width, height, x_offset, y_offset;
|
||||||
union { uint32_t cursor_movement, compose_mode; };
|
union { uint32_t cursor_movement, compose_mode; };
|
||||||
union { uint32_t cell_x_offset; };
|
union { uint32_t cell_x_offset; };
|
||||||
|
|
@ -71,7 +71,7 @@ typedef struct {
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t gap, id, width, height, x, y, base_frame_id, bgcolor;
|
uint32_t gap, id, width, height, x, y, base_frame_id, bgcolor;
|
||||||
bool is_opaque, is_4byte_aligned, alpha_blend;
|
bool is_opaque, is_4byte_aligned, alpha_blend, memory_only;
|
||||||
} Frame;
|
} Frame;
|
||||||
|
|
||||||
typedef enum { ANIMATION_STOPPED = 0, ANIMATION_LOADING = 1, ANIMATION_RUNNING = 2} AnimationState;
|
typedef enum { ANIMATION_STOPPED = 0, ANIMATION_LOADING = 1, ANIMATION_RUNNING = 2} AnimationState;
|
||||||
|
|
|
||||||
8
kitty/parse-graphics-command.h
generated
8
kitty/parse-graphics-command.h
generated
|
|
@ -46,6 +46,7 @@ static inline void parse_graphics_code(PS *self, uint8_t *parser_buf,
|
||||||
unicode_placement = 'U',
|
unicode_placement = 'U',
|
||||||
parent_id = 'P',
|
parent_id = 'P',
|
||||||
parent_placement_id = 'Q',
|
parent_placement_id = 'Q',
|
||||||
|
no_disk_cache = 'N',
|
||||||
offset_from_parent_x = 'H',
|
offset_from_parent_x = 'H',
|
||||||
offset_from_parent_y = 'V'
|
offset_from_parent_y = 'V'
|
||||||
};
|
};
|
||||||
|
|
@ -141,6 +142,9 @@ static inline void parse_graphics_code(PS *self, uint8_t *parser_buf,
|
||||||
case parent_placement_id:
|
case parent_placement_id:
|
||||||
value_state = UINT;
|
value_state = UINT;
|
||||||
break;
|
break;
|
||||||
|
case no_disk_cache:
|
||||||
|
value_state = UINT;
|
||||||
|
break;
|
||||||
case offset_from_parent_x:
|
case offset_from_parent_x:
|
||||||
value_state = INT;
|
value_state = INT;
|
||||||
break;
|
break;
|
||||||
|
|
@ -299,6 +303,7 @@ static inline void parse_graphics_code(PS *self, uint8_t *parser_buf,
|
||||||
U(unicode_placement);
|
U(unicode_placement);
|
||||||
U(parent_id);
|
U(parent_id);
|
||||||
U(parent_placement_id);
|
U(parent_placement_id);
|
||||||
|
U(no_disk_cache);
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -359,7 +364,7 @@ static inline void parse_graphics_code(PS *self, uint8_t *parser_buf,
|
||||||
|
|
||||||
REPORT_VA_COMMAND(
|
REPORT_VA_COMMAND(
|
||||||
"K s {sc sc sc sc sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI "
|
"K s {sc sc sc sc sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI sI "
|
||||||
"sI sI sI sI si si si ss#}",
|
"sI sI sI sI sI si si si ss#}",
|
||||||
self->window_id, "graphics_command",
|
self->window_id, "graphics_command",
|
||||||
|
|
||||||
"action", g.action, "delete_action", g.delete_action, "transmission_type",
|
"action", g.action, "delete_action", g.delete_action, "transmission_type",
|
||||||
|
|
@ -379,6 +384,7 @@ static inline void parse_graphics_code(PS *self, uint8_t *parser_buf,
|
||||||
(unsigned int)g.cursor_movement, "unicode_placement",
|
(unsigned int)g.cursor_movement, "unicode_placement",
|
||||||
(unsigned int)g.unicode_placement, "parent_id", (unsigned int)g.parent_id,
|
(unsigned int)g.unicode_placement, "parent_id", (unsigned int)g.parent_id,
|
||||||
"parent_placement_id", (unsigned int)g.parent_placement_id,
|
"parent_placement_id", (unsigned int)g.parent_placement_id,
|
||||||
|
"no_disk_cache", (unsigned int)g.no_disk_cache,
|
||||||
|
|
||||||
"z_index", (int)g.z_index, "offset_from_parent_x",
|
"z_index", (int)g.z_index, "offset_from_parent_x",
|
||||||
(int)g.offset_from_parent_x, "offset_from_parent_y",
|
(int)g.offset_from_parent_x, "offset_from_parent_y",
|
||||||
|
|
|
||||||
|
|
@ -386,6 +386,20 @@ def test_suppressing_gr_command_responses(self):
|
||||||
self.assertIsNone(li(payload='2' * 12, z=77, m=1, q=2))
|
self.assertIsNone(li(payload='2' * 12, z=77, m=1, q=2))
|
||||||
self.assertIsNone(li(payload='2' * 12))
|
self.assertIsNone(li(payload='2' * 12))
|
||||||
|
|
||||||
|
def test_no_disk_cache_graphics_image(self):
|
||||||
|
s, g, pl, sl = load_helpers(self)
|
||||||
|
self.assertEqual(g.disk_cache.end_of_data_offset(), 0)
|
||||||
|
self.ae(pl('abc', s=1, v=1, f=24, N=1), 'OK')
|
||||||
|
self.assertTrue(g.disk_cache.wait_for_write())
|
||||||
|
self.assertEqual(g.disk_cache.end_of_data_offset(), 0)
|
||||||
|
img = g.image_for_client_id(1)
|
||||||
|
self.assertIsNotNone(img)
|
||||||
|
self.ae(img['data'], b'abc')
|
||||||
|
|
||||||
|
self.ae(pl('def', s=1, v=1, f=24, i=2), 'OK')
|
||||||
|
self.assertTrue(g.disk_cache.wait_for_write())
|
||||||
|
self.assertGreater(g.disk_cache.end_of_data_offset(), 0)
|
||||||
|
|
||||||
def test_load_images(self):
|
def test_load_images(self):
|
||||||
s, g, pl, sl = load_helpers(self)
|
s, g, pl, sl = load_helpers(self)
|
||||||
self.assertEqual(g.disk_cache.total_size, 0)
|
self.assertEqual(g.disk_cache.total_size, 0)
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ type GraphicsCommand struct {
|
||||||
d GRT_d
|
d GRT_d
|
||||||
U GRT_U
|
U GRT_U
|
||||||
|
|
||||||
s, v, S, O, x, y, w, h, X, Y, c, r uint64
|
s, v, S, O, x, y, w, h, X, Y, c, r, N uint64
|
||||||
|
|
||||||
i, I, p uint32
|
i, I, p uint32
|
||||||
|
|
||||||
|
|
@ -176,6 +176,7 @@ func (self *GraphicsCommand) serialize_non_default_fields() (ans []string) {
|
||||||
write_key('U', self.U, null.U)
|
write_key('U', self.U, null.U)
|
||||||
write_key('d', self.d, null.d)
|
write_key('d', self.d, null.d)
|
||||||
|
|
||||||
|
write_key('N', self.N, null.N)
|
||||||
write_key('s', self.s, null.s)
|
write_key('s', self.s, null.s)
|
||||||
write_key('v', self.v, null.v)
|
write_key('v', self.v, null.v)
|
||||||
write_key('S', self.S, null.S)
|
write_key('S', self.S, null.S)
|
||||||
|
|
@ -376,6 +377,8 @@ func (self *GraphicsCommand) SetString(key byte, value string) (err error) {
|
||||||
err = set_val(&self.U, GRT_U_from_string, value)
|
err = set_val(&self.U, GRT_U_from_string, value)
|
||||||
case 'd':
|
case 'd':
|
||||||
err = set_val(&self.d, GRT_d_from_string, value)
|
err = set_val(&self.d, GRT_d_from_string, value)
|
||||||
|
case 'N':
|
||||||
|
err = set_uval(&self.N, value)
|
||||||
case 's':
|
case 's':
|
||||||
err = set_uval(&self.s, value)
|
err = set_uval(&self.s, value)
|
||||||
case 'v':
|
case 'v':
|
||||||
|
|
@ -753,6 +756,19 @@ func (self *GraphicsCommand) SetFrameToMakeCurrent(c uint64) *GraphicsCommand {
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *GraphicsCommand) NoDiskCache() bool {
|
||||||
|
return self.N != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *GraphicsCommand) SetNoDiskCache(noDiskCache bool) *GraphicsCommand {
|
||||||
|
if noDiskCache {
|
||||||
|
self.N = 1
|
||||||
|
} else {
|
||||||
|
self.N = 0
|
||||||
|
}
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
func (self *GraphicsCommand) ImageId() uint32 {
|
func (self *GraphicsCommand) ImageId() uint32 {
|
||||||
return self.i
|
return self.i
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue