diff options
author | Romain Guy <romainguy@google.com> | 2011-02-02 20:28:09 -0800 |
---|---|---|
committer | Romain Guy <romainguy@google.com> | 2011-02-02 20:28:09 -0800 |
commit | 09b7c91de73b59aa3f679b3ae3ba299f82ec9f8a (patch) | |
tree | 9eb49f2fedb60e6df37b54216ed20f054a6f55fd | |
parent | 62687ec12cb8e0b1d4044a235b1387b9a8c3b4b4 (diff) | |
download | frameworks_base-09b7c91de73b59aa3f679b3ae3ba299f82ec9f8a.zip frameworks_base-09b7c91de73b59aa3f679b3ae3ba299f82ec9f8a.tar.gz frameworks_base-09b7c91de73b59aa3f679b3ae3ba299f82ec9f8a.tar.bz2 |
Allocate layers from the layers pool.
Bug #3413433
This change will be beneficial to Launcher to avoid hiccups when
swiping pages of icons. When a layer is discarded, it is kept
in the layers pool instead of being destroyed right away. This
favors memory reuse over allocations.
Change-Id: Ifb6944ba83d6ceb67c331527c0827b26ce648eb1
-rw-r--r-- | core/java/android/view/GLES20Layer.java | 7 | ||||
-rw-r--r-- | libs/hwui/Caches.cpp | 8 | ||||
-rw-r--r-- | libs/hwui/LayerCache.cpp | 25 | ||||
-rw-r--r-- | libs/hwui/LayerCache.h | 11 | ||||
-rw-r--r-- | libs/hwui/LayerRenderer.cpp | 128 | ||||
-rw-r--r-- | libs/hwui/Properties.h | 6 | ||||
-rw-r--r-- | tests/HwAccelerationTest/AndroidManifest.xml | 11 | ||||
-rw-r--r-- | tests/HwAccelerationTest/res/layout/view_layers_6.xml | 60 | ||||
-rw-r--r-- | tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity6.java | 131 |
9 files changed, 306 insertions, 81 deletions
diff --git a/core/java/android/view/GLES20Layer.java b/core/java/android/view/GLES20Layer.java index 0230430..6000a4a 100644 --- a/core/java/android/view/GLES20Layer.java +++ b/core/java/android/view/GLES20Layer.java @@ -66,10 +66,11 @@ class GLES20Layer extends HardwareLayer { @Override void resize(int width, int height) { if (!isValid() || width <= 0 || height <= 0) return; - if (width > mLayerWidth || height > mLayerHeight) { - mWidth = width; - mHeight = height; + mWidth = width; + mHeight = height; + + if (width != mLayerWidth || height != mLayerHeight) { int[] layerInfo = new int[2]; GLES20Canvas.nResizeLayer(mLayer, width, height, layerInfo); diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index bffab95..ebf7aa0 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -20,6 +20,7 @@ #include "Caches.h" #include "Properties.h" +#include "LayerRenderer.h" namespace android { @@ -116,12 +117,7 @@ void Caches::clearGarbage() { size_t count = mLayerGarbage.size(); for (size_t i = 0; i < count; i++) { Layer* layer = mLayerGarbage.itemAt(i); - if (layer) { - if (layer->fbo) glDeleteFramebuffers(1, &layer->fbo); - if (layer->texture) glDeleteTextures(1, &layer->texture); - - delete layer; - } + LayerRenderer::destroyLayer(layer); } mLayerGarbage.clear(); } diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp index 7667af5..a9710ad 100644 --- a/libs/hwui/LayerCache.cpp +++ b/libs/hwui/LayerCache.cpp @@ -128,6 +128,31 @@ Layer* LayerCache::get(const uint32_t width, const uint32_t height) { return layer; } +bool LayerCache::resize(Layer* layer, const uint32_t width, const uint32_t height) { + // TODO: We should be smarter and see if we have a texture of the appropriate + // size already in the cache, and reuse it instead of creating a new one + + LayerEntry entry(width, height); + if (entry.mWidth <= layer->width && entry.mHeight <= layer->height) { + return true; + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, layer->texture); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, entry.mWidth, entry.mHeight, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + if (glGetError() != GL_NO_ERROR) { + return false; + } + + layer->width = entry.mWidth; + layer->height = entry.mHeight; + + return true; +} + bool LayerCache::put(Layer* layer) { const uint32_t size = layer->width * layer->height * 4; // Don't even try to cache a layer that's bigger than the cache diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h index 1333a73..d2d5f39 100644 --- a/libs/hwui/LayerCache.h +++ b/libs/hwui/LayerCache.h @@ -76,6 +76,17 @@ public: * Clears the cache. This causes all layers to be deleted. */ void clear(); + /** + * Resize the specified layer if needed. + * + * @param layer The layer to resize + * @param width The new width of the layer + * @param height The new height of the layer + * + * @return True if the layer was resized or nothing happened, false if + * a failure occurred during the resizing operation + */ + bool resize(Layer* layer, const uint32_t width, const uint32_t height); /** * Sets the maximum size of the cache in bytes. diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index 60ff0bf..24f9739 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -18,6 +18,7 @@ #include <ui/Rect.h> +#include "LayerCache.h" #include "LayerRenderer.h" #include "Properties.h" #include "Rect.h" @@ -34,21 +35,24 @@ void LayerRenderer::prepareDirty(float left, float top, float right, float botto glBindFramebuffer(GL_FRAMEBUFFER, mLayer->fbo); + const float width = mLayer->layer.getWidth(); + const float height = mLayer->layer.getHeight(); + #if RENDER_LAYERS_AS_REGIONS Rect dirty(left, top, right, bottom); if (dirty.isEmpty() || (dirty.left <= 0 && dirty.top <= 0 && - dirty.right >= mLayer->width && dirty.bottom >= mLayer->height)) { + dirty.right >= width && dirty.bottom >= height)) { mLayer->region.clear(); - dirty.set(0.0f, 0.0f, mLayer->width, mLayer->height); + dirty.set(0.0f, 0.0f, width, height); } else { - dirty.intersect(0.0f, 0.0f, mLayer->width, mLayer->height); + dirty.intersect(0.0f, 0.0f, width, height); android::Rect r(dirty.left, dirty.top, dirty.right, dirty.bottom); mLayer->region.subtractSelf(r); } OpenGLRenderer::prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, opaque); #else - OpenGLRenderer::prepareDirty(0.0f, 0.0f, mLayer->width, mLayer->height, opaque); + OpenGLRenderer::prepareDirty(0.0f, 0.0f, width, height, opaque); #endif } @@ -162,64 +166,56 @@ void LayerRenderer::generateMesh() { Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque) { LAYER_RENDERER_LOGD("Creating new layer %dx%d", width, height); - Layer* layer = new Layer(width, height); - - GLuint previousFbo; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo); - - glGenFramebuffers(1, &layer->fbo); - glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo); - - if (glGetError() != GL_NO_ERROR) { - glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); - glDeleteBuffers(1, &layer->fbo); - return 0; + GLuint fbo = Caches::getInstance().fboCache.get(); + if (!fbo) { + LOGW("Could not obtain an FBO"); + return NULL; } glActiveTexture(GL_TEXTURE0); - glGenTextures(1, &layer->texture); - glBindTexture(GL_TEXTURE_2D, layer->texture); + Layer* layer = Caches::getInstance().layerCache.get(width, height); + if (!layer) { + LOGW("Could not obtain a layer"); + return NULL; + } - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + layer->fbo = fbo; + layer->layer.set(0.0f, 0.0f, width, height); + layer->texCoords.set(0.0f, height / float(layer->height), + width / float(layer->width), 0.0f); + layer->alpha = 255; + layer->mode = SkXfermode::kSrcOver_Mode; + layer->blend = !isOpaque; + layer->colorFilter = NULL; + layer->region.clear(); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + GLuint previousFbo; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo); + glBindTexture(GL_TEXTURE_2D, layer->texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); + // Initialize the texture if needed + if (layer->empty) { + layer->empty = false; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->width, layer->height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); - if (glGetError() != GL_NO_ERROR) { - glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); - glDeleteBuffers(1, &layer->fbo); - glDeleteTextures(1, &layer->texture); - delete layer; - return 0; + if (glGetError() != GL_NO_ERROR) { + LOGD("Could not allocate texture"); + glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + glDeleteTextures(1, &layer->texture); + Caches::getInstance().fboCache.put(fbo); + delete layer; + return NULL; + } } glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, layer->texture, 0); - if (glGetError() != GL_NO_ERROR) { - glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); - glDeleteBuffers(1, &layer->fbo); - glDeleteTextures(1, &layer->texture); - delete layer; - return 0; - } - glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); - layer->layer.set(0.0f, 0.0f, width, height); - layer->texCoords.set(0.0f, 1.0f, 1.0f, 0.0f); - layer->alpha = 255; - layer->mode = SkXfermode::kSrcOver_Mode; - layer->blend = !isOpaque; - layer->empty = false; - layer->colorFilter = NULL; - return layer; } @@ -227,27 +223,17 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) { if (layer) { LAYER_RENDERER_LOGD("Resizing layer fbo = %d to %dx%d", layer->fbo, width, height); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, layer->texture); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); - - if (glGetError() != GL_NO_ERROR) { - glDeleteBuffers(1, &layer->fbo); - glDeleteTextures(1, &layer->texture); - - layer->width = 0; - layer->height = 0; - layer->fbo = 0; - layer->texture = 0; - + if (Caches::getInstance().layerCache.resize(layer, width, height)) { + layer->layer.set(0.0f, 0.0f, width, height); + layer->texCoords.set(0.0f, height / float(layer->height), + width / float(layer->width), 0.0f); + } else { + if (layer->texture) glDeleteTextures(1, &layer->texture); + delete layer; return false; } - - layer->width = width; - layer->height = height; } + return true; } @@ -255,10 +241,16 @@ void LayerRenderer::destroyLayer(Layer* layer) { if (layer) { LAYER_RENDERER_LOGD("Destroying layer, fbo = %d", layer->fbo); - if (layer->fbo) glDeleteFramebuffers(1, &layer->fbo); - if (layer->texture) glDeleteTextures(1, &layer->texture); + if (layer->fbo) { + Caches::getInstance().fboCache.put(layer->fbo); + } - delete layer; + if (!Caches::getInstance().layerCache.put(layer)) { + if (layer->texture) glDeleteTextures(1, &layer->texture); + delete layer; + } else { + layer->region.clear(); + } } } diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 2f230b5..2d8b6f3 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -64,14 +64,14 @@ enum DebugLevel { // Converts a number of mega-bytes into bytes #define MB(s) s * 1024 * 1024 -#define DEFAULT_TEXTURE_CACHE_SIZE 20.0f -#define DEFAULT_LAYER_CACHE_SIZE 8.0f +#define DEFAULT_TEXTURE_CACHE_SIZE 24.0f +#define DEFAULT_LAYER_CACHE_SIZE 24.0f #define DEFAULT_PATH_CACHE_SIZE 4.0f #define DEFAULT_SHAPE_CACHE_SIZE 1.0f #define DEFAULT_PATCH_CACHE_SIZE 512 #define DEFAULT_GRADIENT_CACHE_SIZE 0.5f #define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f -#define DEFAULT_FBO_CACHE_SIZE 12 +#define DEFAULT_FBO_CACHE_SIZE 16 #define DEFAULT_TEXT_GAMMA 1.4f #define DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD 64 diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index fc50334..3535809 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -96,7 +96,16 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> - + + <activity + android:name="ViewLayersActivity6" + android:label="_ViewLayers6"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity android:name="AlphaLayersActivity" android:label="_αLayers"> diff --git a/tests/HwAccelerationTest/res/layout/view_layers_6.xml b/tests/HwAccelerationTest/res/layout/view_layers_6.xml new file mode 100644 index 0000000..36cf8c9 --- /dev/null +++ b/tests/HwAccelerationTest/res/layout/view_layers_6.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout + android:orientation="vertical" + android:layout_width="0dip" + android:layout_height="match_parent" + android:layout_weight="1"> + + <Button + android:onClick="enableLayer" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Enable layer" /> + + <Button + android:onClick="disableLayer" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Disable layer" /> + + <Button + android:onClick="shrinkLayer" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Shrink layer" /> + + <Button + android:onClick="growLayer" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Grow layer" /> + + </LinearLayout> + + <ListView + android:id="@+id/list1" + android:layout_width="0dip" + android:layout_height="match_parent" + android:layout_weight="1" /> + +</LinearLayout> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity6.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity6.java new file mode 100644 index 0000000..2edfec7 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity6.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.hwui; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.os.Bundle; +import android.util.DisplayMetrics; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.TextView; + +@SuppressWarnings({"UnusedDeclaration"}) +public class ViewLayersActivity6 extends Activity { + private final Paint mPaint = new Paint(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.view_layers_6); + + mPaint.setColorFilter(new PorterDuffColorFilter(0xff00ff00, PorterDuff.Mode.MULTIPLY)); + + setupList(R.id.list1); + } + + public void enableLayer(View v) { + findViewById(R.id.list1).setLayerType(View.LAYER_TYPE_HARDWARE, mPaint); + } + + public void disableLayer(View v) { + findViewById(R.id.list1).setLayerType(View.LAYER_TYPE_NONE, null); + } + + public void growLayer(View v) { + findViewById(R.id.list1).getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT; + findViewById(R.id.list1).requestLayout(); + } + + public void shrinkLayer(View v) { + findViewById(R.id.list1).getLayoutParams().height = 300; + findViewById(R.id.list1).requestLayout(); + } + + private void setupList(int listId) { + final ListView list = (ListView) findViewById(listId); + list.setAdapter(new SimpleListAdapter(this)); + } + + private static class SimpleListAdapter extends ArrayAdapter<String> { + public SimpleListAdapter(Context context) { + super(context, android.R.layout.simple_list_item_1, DATA_LIST); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + TextView v = (TextView) super.getView(position, convertView, parent); + final Resources r = getContext().getResources(); + final DisplayMetrics metrics = r.getDisplayMetrics(); + v.setCompoundDrawablePadding((int) (6 * metrics.density + 0.5f)); + v.setCompoundDrawablesWithIntrinsicBounds(r.getDrawable(R.drawable.icon), + null, null, null); + return v; + } + } + + private static final String[] DATA_LIST = { + "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra", + "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina", + "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan", + "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", + "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia", + "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", + "British Indian Ocean Territory", "British Virgin Islands", "Brunei", "Bulgaria", + "Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde", + "Cayman Islands", "Central African Republic", "Chad", "Chile", "China", + "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo", + "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic", + "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic", + "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea", + "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland", + "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia", + "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar", + "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau", + "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary", + "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica", + "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos", + "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg", + "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", + "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova", + "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", + "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", + "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas", + "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru", + "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar", + "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena", + "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon", + "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal", + "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands", + "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea", + "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden", + "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas", + "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", + "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda", + "Ukraine", "United Arab Emirates", "United Kingdom", + "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan", + "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara", + "Yemen", "Yugoslavia", "Zambia", "Zimbabwe" + }; +} |